- Find Elements in a Contaminated Binary Tree
Obvious dfs. Problem is how python code is written: gives me a revision on self.
selfの使い方
インスタンス変数として参照する
まずはじめに、コンストラクタで使用する例を見てみましょう。pythonにおけるコンストラクタは”init“と表記します。1
2
3
4
5
6
7
8class className():
def __init__(self, strA, strB):
self.strA = strA
self.strB = strB
test = className("Hello", "World!")
print(test.strA)
print(test.strB)
クラス変数として参照する
また、以下のようにクラス変数として別メソッドでも使う事ができます。1
2
3
4
5
6
7
8
9
10
11class className():
def __init__(self, strA, strB):
self.strA = strA
self.strB = strB
def output(self):
print(self.strA)
print(self.strB)
test = className("Hello", "World!")
test.output()
1 | Hello |
クラス継承の際も使える1
2
3
4
5
6
7
8
9
10class classA():
def __init__(self):
self.strA = "Hello World!"
class classB(classA):
def output(self):
print(self.strA)
test = classB()
test.output()
Achtung⚠️
1 | # -*- coding: utf-8 -*- |
1 | 1: Hello python |
可以看出, self
总是与类相关,或者类创建出来的instance. 所以1261对应的python写法是:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class FindElements:
def __init__(self, root: TreeNode):
self.seen = set()
def dfs(node: TreeNode, v: int) -> None:
if node:
node.val = v
self.seen.add(v)
dfs(node.left, 2 * v + 1)
dfs(node.right, 2 * v + 2)
dfs(root, 0)
def find(self, target: int) -> bool:
return target in self.seen
# Your FindElements object will be instantiated and called as such:
# obj = FindElements(root)
# param_1 = obj.find(target)
上面的写法的几点说明:
__init__
和find
方法第一个参数都是self, 是因为他们都是类方法, 所以第一参数是self.dfs
并不是类方法,不会实例化对象代指对象本身赋值属性, 所以不需要self,调用dfs也不需要self;- 但是外部方法变量
seen
是self.seen
相当于全局变量, 所以内部方法调用的时候还是要用self.seen
. - 同理,
find
调用同级函数,类方法__init__
中的seen
的时候,也需要self.
总结一下: 和普通数相比,在类中定义函数只有一点不同,就是第一参数永远是类的本身实例变量self,并且调用时,不用传递该参数。除此之外,类的方法(函数)和普通函数没啥区别
一个更一般出现的例子和类属性的私有化
例子:1
2
3
4
5
6class Student(obiect):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print "%s: %s" % (self.name, self.score)
1 | >>>student = Student("Lei", 99) |
如果让内部属性不被外部访问: 属性前面加2个下划线: instance变量如果以__
两个下划线开头,那么外部就不能访问;1
2
3
4
5
6class Student(obiect):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print "%s: %s" % (self.name, self.score)
只是实例变量加了两个下划线,但是外部已经不能访问了1
2
3
4
5>>> student = Student('Lei', 99)
>>> student.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
这样外部代码就不能修改对象内部的状态,增加了访问限制,代码robust
那如果外部代码要获取name
和score
呢? 给类增加get_name
, get_score
:1
2
3
4
5
6
7class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
要修改呢? 增加set
:1
2
3
4
5class Student(object):
...
def set_score(self, score):
self.__score = score
变量名双下划线开头并结尾的, 类似__xxx__
的是特殊变量,不是private,不要自定义这种.
有些时候,会看到以一个下划线开头的实例变量名,比如_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
(其实很无语, 这种单下划线想表明是private但是实际上没有用的,js中也有)
self
deep dive
self
代表类的实例,不是类本身1
2
3
4
5
6
7
8
9
10
11class Test:
def ppr(self):
print(self)
print(self.__class__)
t = Test()
t.ppr()
执行结果:
<__main__.Test object at 0x000000000284E080>
<class '__main__.Test'>
从上面的例子看出, self
代表类的实例,而self.__class__
指向类.self
可以换成this
.
self
代表类的实例 如果类的实例要用这个方法,self必须写,如果定义和调用都不传递类实例是可以的,这是类方法
不写self
但是是用实例调用:1
2
3
4
5
6class Test:
def ppr():
print(self)
t = Test()
t.ppr()
调用t.ppr
时, 实际上python解释成了Test.ppr(t)
, 也就是self
替换成了类实例.
Result:1
2
3
4Traceback (most recent call last):
File "cl.py", line 6, in <module>
t.ppr()
TypeError: ppr() takes 0 positional arguments but 1 was given
因为调用t.ppr
时python解释成了Test.ppr(t)
, 所以“多传递了一个参数”, 说明实例方法的地方,self
在定义实例方法时不可以省略。
当然,如果定义和调用都不传递实例,是可以的,就是类方法;1
2
3
4
5
6
7
8class Test:
def ppr():
print(__class__)
Test.ppr()
运行结果:
<class '__main__.Test'>
Java version:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class FindElements {
private Set<Integer> seen = new HashSet<>();
public FindElements(TreeNode root) {
dfs(root, 0);
}
private void dfs(TreeNode node, int val) {
if (node == null) return;
seen.add(val);
node.val = val;
dfs(node.left, 2 * val + 1);
dfs(node.right, 2 * val + 2);
}
public boolean find(int target) {
return seen.contains(target);
}
}
/**
* Your FindElements object will be instantiated and called as such:
* FindElements obj = new FindElements(root);
* boolean param_1 = obj.find(target);
*/