Python中有些方法和属性名前后都添加了双下划线,它们都属于特殊方法和属性,开发者可以通过它们来实现特殊功能,最常见的就是前面的__init__构造方法。
# 常见特殊方法
# __repr__
该方法类似于Java中的ToString(),默认是输出"类名 object at Address",可以自己修改:
class CustomClass():
name=None
def __init__(self,name):
self.name=name
def __repr__(self):
return "this obj name is:"+self.name
c1 = CustomClass("li")
c2 = CustomClass("wang")
print(c1)
print(c2)
2
3
4
5
6
7
8
9
10
11
12
# __del__
这个方法在Python删除对象时自动执行,注意一个对象有多个引用时,只删除其中的一个引用垃圾回收并不会回收对象,只有该对象的所有引用都删除后才会回收该对象所占用的内存空间。注意父类有这个方法,子类要重写时,需要显式调用父类的__del__方法。
class DelClass:
def __init__(self,name):
self.name=name
def __del__(self):
print("删除对象:%s" % self.name)
c1 = DelClass("c1")
c2 = c1
del c1
#del c2
print("--------program end-------")
2
3
4
5
6
7
8
9
10
11
12
13
# __dir__
该方法列出对象内部的所有属性方法名,它是包含所有属性方法名的一个序列。当程序对某个对象执行dir(对象名)时实际上是调用该对象的__dir__()方法,该方法会返回内置属性和方法。
class ClassDir:
def __init__(self,name):
self.name=name
def __dir__(self):
return "hello"
c1 = ClassDir("li")
print(dir(c1))
2
3
4
5
6
7
8
# __dict__
该属性用于查看对象属性名及其值。
class ClassDict:
age=10
def __init__(self,name):
self.name=name
self.score=100
def GetClass(self):
return "hi"
c1 = ClassDict("li")
print(ClassDict.__dict__)
print(c1.__dict__)
print(c1.age)
2
3
4
5
6
7
8
9
10
11
12
13
注意以对象名c1调用该属性时并不输出age属性,以其类名调用时会输出age属性,这点要注意。
# __getattr__,__setattr___
前面类中提到过设置属性,当获取或设置一个不存在的属性时,就会调用这些方法:
class ClassAttr:
@property
def Age(self):
print("get Age:%s" % self.age)
return self.age
@Age.setter
def Age(self,age):
print("set Age:%s" % age)
self.age=age
@Age.deleter
def Age(self):
print("delete Age!")
del self.age
#def __getattribute__(self,name):
# return "get already existed attr:%s" % name
def __getattr__(self,name):
return "get value:%s" % name
def __setattr__(self,name,value):
print("set attr in general access: %s,%s" % (name,value))
def __delattr__(self,name):
print("delete attr in general access: %s" % name)
c1 = ClassAttr()
c1.Age=100
c1.age=200
print(c1.Age)
print(c1.score)
del c1.Age
del c1.score
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
35
36
37
38
39
从上面可以看出:
- __getattribute__,__setattr__,__delattr__会覆盖自己定义属性的getter,setter和deleter方法。
- 在没有__getattribute__时,在获取任何字段时会先调用__getattr__方法。
# 反射相关的方法
反射指的是在运行过程中能够知道这个类的属性和方法,并且使用它们,在程序中动态获取类的相关信息并修改的这种机制被称为反射。
class TestReflect:
name="TestReflectClass"
def GetName(self):
print("this is TestReflect,name:%s" % self.name)
c1 = TestReflect()
print(hasattr(c1,"name"))
print(hasattr(c1,"Getname"))
print(hasattr(c1,"GetName"))
print(getattr(c1,"name"))
setattr(c1,"name","hello world!")
print(getattr(c1,"name"))
print(getattr(c1,"GetName"))
def DGetName():
print("this is outside define method" )
setattr(c1,"DGetName",DGetName)
c1.DGetName()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上面涉及三个方法:
- hasattr检查某个对象有没有包含指定属性或方法。
- getattr获取对象的某个属性值。
- setattr设置对象的某个属性值。
# __call__
使用hasattr只能判断有没有这个属性或方法,但无法具体判断是方法还是属性,这时候就需要判断对象是否有__call__属性。
class TestReflect:
name="TestReflectClass"
def GetName(self):
print("this is TestReflect,name:%s" % self.name)
def __call__(self):
print("TestReflect is called")
c1 = TestReflect()
print(hasattr(c1.name,'__call__'))
print(hasattr(c1.GetName,'__call__'))
c1.GetName.__call__()
c1.GetName()
c1()
def TestFunc():
print("this is a function!")
TestFunc.__call__()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 序列相关
使用类完善几个内置方法可以实现跟序列一样的功能:
class TestList:
def __init__(self):
self.__list={}
def __len__(self):
return len(self.__list)
def __getitem__(self,key):
return self.__list[key]
def __setitem__(self,key,value):
self.__list[key]=value
def __delitem__(self,key):
del self.__list[key]
def __contains__(self,item):
print("------search the value------")
for elem in self.__list:
if self.__list[elem] == item :
return elem
return False
def GetAllElem(self):
for elem in self.__list:
print(elem)
c1 = TestList()
c1["math"]=100
c1["english"]=60
c1["history"]=200
print(c1["math"])
print(len(c1))
print(200 in c1)
del c1["math"]
c1.GetAllElem()
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
上面有几个关键方法:
- __len__ 得到该容器中元素个数
- __getitem__ 得到容器中的某个元素
- __contains__ 该容器是否包含某个元素
- __setitem__ 设置指定键的值
- __delitem__ 删除指定的元素
# 迭代器
前面使用for-in循环遍历列表,元组和字典,这些对象都是可迭代的,如果我们希望自己定义的对象也是可迭代的,需要实现下面两个方法:
- iter(self):这个方法返回一个迭代器,这个迭代器需要有一个__next__()方法。如果没有这个方法,这个对象是不能用for-in来进行迭代的,因为这意味着它不可迭代。
- reversed(self):为reversed()函数提供支持。
class IterObj:
def __init__(self,num):
self.arr=[]
for i in range(num):
self.arr.append(i*3)
self.sum=0
def __next__(self):
print("进入迭代")
if len(self.arr) == 0:
raise StopIteration
self.sum += self.arr[0]
del self.arr[0]
return self.sum
def __iter__(self):
return self
def __reversed__(self):
print("hello")
c1 = IterObj(5)
print(next(c1))
print(next(c1))
for item in c1:
print(item)
reversed(c1)
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
前面说活列表元组和字典,如果想基于它们定义自己的迭代器,可以继承它们然后自己再添加一些方法。
# 生成器
和迭代器类似,它也有__next__()方法,这意味着对它可以使用next()。
迭代器是先定义一个迭代器类,然后通过实例来创建迭代器,这意味着这个对象可以迭代;而生成器则是先定义一个包含yield语句的函数,以此来创建生成器。
def Generator():
for i in range(10):
yield i
gen = Generator()
for i in gen:
print(i)
2
3
4
5
6
7
8
生成器有个好处就是它是按需获取的,这意味着只有每次调用next()获取下个数据时生成器才会执行一次,这为程序提高了性能,而且如果不使用生成器就需要使用列表或元组来存放这些数据,当这些数据很大时,就会有不可忽略的内存开销,所以生成器也节省了内存开销。
如何同生成器交换数据呢?
def Generator(val):
for i in range(val):
returnVal = yield i
print("returnVal:%s" % returnVal)
gen = Generator(10)
print("value:%s" % next(gen))
print("--------------")
print("value:%s" % next(gen))
print("--------------")
print("value:%s" % gen.send(5))
print("--------------")
print("value:%s" % next(gen))
2
3
4
5
6
7
8
9
10
11
12
13
14
send(val)相当于(yield i)=val,再来看个复杂点的:
def Generator(val):
flag =None
for i in range(val):
temp = (yield flag) if flag is not None else (yield i)
#(yield flag)如果去掉括号这个语句执行效果会不同。
print("val:%s,flag:%s,temp:%s" % (val,flag,temp))
gen = Generator(10)
print("value:%s" % next(gen))
print("--------------")
print("value:%s" % next(gen))
print("--------------")
print("value:%s" % gen.send(5))
print("--------------")
print("value:%s" % next(gen))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上面一定要注意就是(yield flag)是加括号的,如果它去掉括号会有不一样的结果。
如果在生成器内想停止迭代怎么办呢?可以使用这两个方法:
def Generator(val):
for i in range(val):
returnVal = yield i
print("returnVal:%s" % returnVal)
gen = Generator(10)
print("value:%s" % next(gen))
print("--------------")
print("value:%s" % next(gen))
print("--------------")
print("value:%s" % gen.send(5))
print("--------------")
#gen.close()
gen.throw(ValueError)
print("value:%s" % next(gen))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 运算符重载
除此之外还有其他一些类的内置方法用来完成特定的功能:
# 数值运算相关
方法 | 运算符 |
---|---|
__add__ | + |
__sub__ | - |
__mul__ | * |
__matmul__ | @ |
__truediv__ | / |
__floordiv__ | // |
__mod__ | % |
__divmod__ | divmod |
__pow__ | ** |
__lshift__ | << |
__rshift__ | >> |
__and__ | & |
__xor__ | ^ |
__or__ | \ |
上面这些操作符默认都是在该类的右边,比如执行x+y时,如果x没有定义__add__,那么要检查y是否定义了__radd__,也就是操作符在y的左边。所以上面的所有方法都还有一个以r为开头的版本,检查操作符在类的左边时执行。
正常情况下"+"有一个"+="版本,上面的方法以i为开头就是进行类似的转换。
# 比较运算符
方法 | 运算符 |
---|---|
__lt__ | < |
__le__ | <= |
__eq__ | == |
__ne__ | != |
__gt__ | > |
__ge__ | >= |
# 单目运算符
方法 | 运算符 |
---|---|
__neg__ | 单目求负 |
__pos__ | 单目求正 |
__invert__ | 单目取反 |
# 类型转换相关
方法 | 函数名 |
---|---|
__str__ | str() |
__bytes__ | bytes() |
__complex__ | complex() |
__int__ | int() |
__float__ | float() |
# 常见内建函数
方法 | 函数名 |
---|---|
__format__ | format() |
__hash__ | hash() |
__abs__ | abs() |
__round__ | round() |
__trunc__ | trunc() |
__floor__ | floor() |
__ceil__ | ceil() |