简述

  • 通过dir()查看类对象或者实例时看到的以双下划线开头和结尾的方法和属性就是特殊方法和特殊属性,基本都是继承自object
  • 通常会去显式的调用特殊资源,除了__init__()以外一般也不会去重写

dict

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

class MyClass(object):
ca = "ca"

def __init__(self):
self.ia = "ia_value"

def im(self):
pass

@classmethod
def cm(cls):
pass

@staticmethod
def sm():
pass

MyClass.ca2 = "ca2"
print(MyClass.__dict__)
# {'__module__': '__main__', 'ca': 'ca', '__init__': <function MyClass.__init__ at 0x1014293a0>, 'im': <function MyClass.im at 0x1014d34c0>, 'cm': <classmethod object at 0x1013d3cd0>, 'sm': <staticmethod object at 0x10144d880>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None, 'ca2': 'ca2'}

mc = MyClass()
mc.ia2 = "ia2"
print(mc.__dict__)
# {'ia': 'ia_value', 'ia2': 'ia2'}
  • 特殊属性__dict__,获取到所有的方法和属性组成的字典,key是资源名称
  • 对象不会显示特殊资源

类反射

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
class MyClass(object):
def __init__(self):
self.x = 1

def do_sth(self):
print("do_sth被调用")


mc = MyClass()

print(hasattr(mc, 'x'))
print(hasattr(mc, 'do_sth'))
print(hasattr(mc, 'y'))

print(getattr(mc, 'x'))

f = getattr(mc, 'do_sth')
f()

# print(getattr(mc, 'y'))
print(getattr(mc, 'y', 2))

setattr(mc, 'z', 3)
print(getattr(mc, 'z'))

setattr(mc, 'z', 4)
print(getattr(mc, 'z'))

delattr(mc, 'z')
print(hasattr(mc, 'z'))
  • python是个动态语言,本身就支持动态的为类对象或者实例对象进行属性和方法的绑定删除操作
  • ==getattr()和直接调用方法的区别是什么呢==

特殊方法__len__()

  • python有一个内置函数len(),但是它只能对内置类的对象使用
  • 自定义的对象中只要实现了__len()__方法,即可使用len()

iter()和__next__()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyClass(object):
def __init__(self):
self.data = 0

def __iter__(self):
return self

def __next__(self):
if self.data > 5:
raise StopIteration()
else:
self.data += 1
return self.data


for item in MyClass():
print(item)
  • 自定义对象想要使用for-in语句,需要对__iter__()和__next__()进行实现

str()和__repr__()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> class MyClass(object):
... def __str__(self):
... return "__str__被调用"
... def __repr__(self):
... return "__repr__被调用"
...
>>> mc = MyClass()
>>> mc
__repr__被调用
>>> print(mc)
__str__被调用
>>> str(mc)
'__str__被调用'
>>> repr(mc)
'__repr__被调用'
  • 对应了两个内置函数str()和repr()
  • 还得说说两个内置函数的区别,str追求输出内容的可读性,repr追求数据的完整性(适合调试,尽量把信息展示齐全)
  • 直接打印一个实例对象时,调用__repr__(),如果没有实现,打印类对象信息和实力对象在内存中的地址
  • print(),查找__str__() -> repr() ->打印内存地址
  • str(),调用此方法其实是创建一个字符串,str() -> repr() ->打印内存地址
  • repr(),repr() ->打印内存地址
  • 通常情况下两个特殊方法的实现是一样的,所以实现一个方法后可以赋值给另一个方法__repr__ = __str__

new()

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
class Parent(object):
def __new__(cls, *args, **kwargs):
print("父类的__new__()被调用,其形参cls对应实参的id:%s" % id(cls))
obj = super().__new__(cls)
print("创建的实例对象的id:%s" % id(obj))
return obj


class Child(Parent):
def __init__(self, name):
print("子类的__init__()被调用,其形参self对应实参的id:%s" % id(self))
self.name = name


print("父类的id:%s" % id(Parent))
print("子类的id:%s" % id(Child))

child = Child("Mike")
print("创建的实例对象的id:%s" % id(child))

# 输出
父类的id:140308712091888
子类的id:140308712092832
父类的__new__()被调用,其形参cls对应实参的id:140308712092832
创建的实例对象的id:4371497552
子类的__init__()被调用,其形参self对应实参的id:4371497552
创建的实例对象的id:4371497552
  • 通过类名()的方式创建实例对象时,执行两个操作
    • 执行__new__()方法,自身实现->父类实现->object实现,通过__new__()创建对象
    • 执行__init_()对创建的对象进行初始化

del()

对象被回收前会先调用这个方法,注意del obj也只是告诉回收器进行回收,具体有没有释放掉不确定,不一定是立即进行回收

getattr()

1
2
3
4
5
6
7
8
9
10
11
12
13
class SomeClass(object):
def __getattr__(self, name):
if name == "data":
return 18
elif name == "do_sth":
return print
raise AttributeError("'SomeClass' object has no attribute '%s'" % name)


sc = SomeClass()
print(sc.data) # 18
sc.do_sth(1, 2, 3) # 1 2 3
print(sc.score) # AttributeError: 'SomeClass' object has no attribute 'score'
  • 访问对象的属性或者方法时,如果资源不存在会抛出AttributeError
  • 如果实现了__getattr__(),则会调用这个特殊方法

getitem()

迭代对象时obj[index]会调用此特殊方法

call()

obj(),把对象当方法调用,奇怪的脑洞

doc()

获取对象的文档字符串,也就是类的注释”””对类的描述说明”””

slots()

动态绑定的时候说过这个方法,python是一个动态语言,可以给对象动态绑定资源,这个特殊方法是为了限制这一点,只有在这个方法中预留的资源才可以进行动态绑定