Python类对象的特殊方法
查看类包含的方法
# coding=utf-8
from pprint import pprint as pp
class A(object):
pass
pp(dir(A))
下面是打印内容,也就是后面探讨的内容,下面让我们一起来看看Python类对象的内建方法都有哪些作用:
['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__']
1.__class__方法
一句话总结:__class__方法等同于Python的内建方法type
# coding=utf-8
from pprint import pprint as pp
class A(object):
pass
print A.__class__, type
打印结果:
<type 'type'> <type 'type'>
这意味着A.__class__和Python的内建方法type是同一个内建方法,所以让我们看看type的这个注释:
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
- type(object) ,返回A的类型,这种用法大家都经常使用;
- type(name, bases, dict),生成新的类型,用于代码中动态生成新的类型,参数name用于指定新生成的类名,参数bases,用于指定继承父类的元组,dict可以动态的为新生成的类指定属性,下面来个例子,用于证明__class__方法与type等效:
# coding=utf-8
from pprint import pprint as pp
class A(object):
def __init__(self):
self.PropertyA = 1
class B(object):
def __init__(self):
self.PropertyB = 2
def B_method(self):
pass
NewA = A.__class__('NewA', (A, B,), {'NewProperty': 'xxxxx'})
new_a = NewA()
print(dir(new_a)[:3]) # 节约篇幅,就不完全打印了
print new_a.__class__.__name__
打印结果:
['B_method', 'NewProperty', 'PropertyA']
NewA
可以看到,新生成的实例new_a继承了父类A的属性PropertyA,父类B的方法B_method,以及自身的新增属性B_method,至于为什么没有继承父类B的属性PropertyB ,有兴趣可以去看看Python新式类关于继承深度优先的说明。
2.__delattr__方法、 __getattribute__方法、__setattr__方法
一句话总结:这三兄弟没什么好说的,就是给Python类对象,增加属性,访问属性,删除属性
3.__dict__方法
一句话总结:以字典的形式返回类实例,类对象的属性,方法,需要注意类实例和类对象的区别
比较简单的方法,直接上代码:
# coding=utf-8
from pprint import pprint as pp
class A(object):
def __init__(self):
self.name = 'A'
self.scope = None
@property
def property1(self):
return 0
@staticmethod
def method_1():
print 'this is m1'
a = A()
A.this_a = 'a'
pp(a.__dict__)
pp(A.__dict__.items())
pp(type(A.__dict__))
打印如下:
{'name': 'A', 'scope': None}
[('property1', <property object at 0x00000000030195E8>),
('__module__', '__main__'),
('method_1', <staticmethod object at 0x000000000301B378>),
('this_a', 'a'),
('__dict__', <attribute '__dict__' of 'A' objects>),
('__weakref__', <attribute '__weakref__' of 'A' objects>),
('__doc__', None),
('__init__', <function __init__ at 0x0000000003022208>)]
<type 'dictproxy'>
看起来这个方法会返回类实例的属性,类对象的静态&动态属性和一些内建方法
有趣的是,以cls.__dict__形式调用时,返回值是一个dictproxy对象。我在内建类type的代码里面只找到一句注释,太长了懒得看:
__dict__ = None # (!) real value is "dict_proxy({'__module__': <attribute '__module__' of 'type' objects>, '__abstractmethods__': <attribute '__abstractmethods__' of 'type' objects>, '__getattribute__': <slot wrapper '__getattribute__' of 'type' objects>, '__weakrefoffset__': <member '__weakrefoffset__' of 'type' objects>, '__dict__': <attribute '__dict__' of 'type' objects>, '__lt__': <slot wrapper '__lt__' of 'type' objects>, '__init__': <slot wrapper '__init__' of 'type' objects>, '__setattr__': <slot wrapper '__setattr__' of 'type' objects>, '__subclasses__': <method '__subclasses__' of 'type' objects>, '__new__': <built-in method __new__ of type object at 0x0000000062F771E0>, '__base__': <member '__base__' of 'type' objects>, '__mro__': <member '__mro__' of 'type' objects>, 'mro': <method 'mro' of 'type' objects>, '__dictoffset__': <member '__dictoffset__' of 'type' objects>, '__call__': <slot wrapper '__call__' of 'type' objects>, '__itemsize__': <member '__itemsize__' of 'type' objects>, '__ne__': <slot wrapper '__ne__' of 'type' objects>, '__instancecheck__': <method '__instancecheck__' of 'type' objects>, '__subclasscheck__': <method '__subclasscheck__' of 'type' objects>, '__gt__': <slot wrapper '__gt__' of 'type' objects>, '__name__': <attribute '__name__' of 'type' objects>, '__eq__': <slot wrapper '__eq__' of 'type' objects>, '__basicsize__': <member '__basicsize__' of 'type' objects>, '__bases__': <attribute '__bases__' of 'type' objects>, '__flags__': <member '__flags__' of 'type' objects>, '__doc__': <attribute '__doc__' of 'type' objects>, '__delattr__': <slot wrapper '__delattr__' of 'type' objects>, '__le__': <slot wrapper '__le__' of 'type' objects>, '__repr__': <slot wrapper '__repr__' of 'type' objects>, '__hash__': <slot wrapper '__hash__' of 'type' objects>, '__ge__': <slot wrapper '__ge__' of 'type' objects>})"
在官方文档里面也找到了它:
PyObject* PyDictProxy_New(PyObject *dict)
Return value: New reference.
Return a proxy object for a mapping which enforces read-only behavior. This is normally used to create a proxy to prevent modification of the dictionary for non-dynamic class types.
New in version 2.2.
也就是说以cls.__dict__这种方式调用时,返回了一个只读属性的DictProxy对象,然后我又仔细想了想,这个只读属性有什么作用
emmmmm,貌似没有什么作用,在上面代码里面可以看见,可以通过cls.new_attr = 1,这种形式给cls增加新的属性,再次调用__dict__时,返回值也就变化了.
4.__doc__方法,__format__方法,__str__方法,__repr__方法,__module__方法
一句话总结:冷门5兄弟,没什么用,但是很好玩
__doc__方法解锁姿势:
# coding=utf-8
from pprint import pprint as pp
class A(object):
__doc__ = 'this is for help, but no help~~\n'
def __init__(self):
self.name = 'A'
self.scope = None
a = A()
print a.__doc__, A.__doc__
打印结果,类和实例都可以调用这个方法:
this is for help, but no help~~
this is for help, but no help~~
至于__format__方法,__str__方法,__repr__方法,就要提到Python对应的三个内建方法:format,str,repr。简单来说,当你使用format(instance\cls)时,会调用cls.__format__或者instance.__format__方法,另外俩也一样。
__module__方法更直接,可以用cls.__module__的形式或者instance.__module__的形式调用,返回类所在的module
当然,具体的每个方法肯定有它应该使用的场景和方法,但是总体来说属于辅助系的功能,对实际生产环境的编码,或者功能实现贡献不大,我贴个官方文档链接,大家有兴趣可以玩一下: docs.python.org
5.__hash__方法
一句话总结:基本用不到,生产环境千万小心使用,否则很快就可以去财务室结算薪水走人
官方文档贴在这里“Called by built-in function hash() and for operations on members of hashed collections including set, frozenset, and dict.”
也就是说,有4种情况会调用这个__hash__方法:
- 由Python的内建函数函数hash()调用
- 操作set类型的成员时;
- 操作frozenset类型的成员时;
- 操作dict类型的成员时;
下面分两类来说一说:
首先是,由Python的内建函数函数hash()调用,这个不多说,这很容易理解。
第二类是操作哈希集合类型的成员,我已dict为例,举个栗子:
# 定义一个dict,一个class A,calss B,以A和B的实例为Key
class A(object):pass
class B(object):pass
a = A()
b = B()
d = dict()
d[a] = 1
d[b] = 2
print d
# 打印结果:
{<__main__.A object at 0x10f360510>: 1, <__main__.B object at 0x10f3604d0>: 2}
现在我们进行第二步,重写A和B的hash方法,让他们返回相同的值,再把它塞到字典d里面,按照我们的预期来说,字典d应该只有一对key-value:
class A(object):
def __hash__(self):
print 'hash a'
return 1
class B(object):
def __hash__(self):
print 'hash b'
return 1
a = A()
b = B()
d = dict()
d[a] = 1
d[b] = 2
print d
# 打印结果
hash a
hash b
{<__main__.A object at 0x108f26f10>: 1, <__main__.B object at 0x108f26f50>: 2}
我们可以看到,在修改dict的成员对象时,对Key值,调用了hash(Key),从而调用了A和B的hash函数,打印出了hash a 和hash b,但是hash值相同的a和b还是成为了字典d的两个键,很明显不符合预期。
猜测一下,操作字典成员时,会判断成员的hash值是否已经在字典中,应该会用到__eq__方法,尝试重写A或者B的eq方法:
class A(object):
def __hash__(self):
print 'hash a'
return 1
def __eq__(self, other):
print 'call eqa'
return True
class B(object):
def __hash__(self):
print 'hash b'
return 1
def __eq__(self, other):
print 'call eqb'
return True
a = A()
b = B()
d = dict()
d[a] = 1
d[b] = 2
print d
# 打印结果
hash a
hash b
call eqb
{<__main__.A object at 0x10d5b2f10>: 2}
OK,看来是符合我们预期结果的,由于hash值相同,所以a和b无法同时作为字典d的key.
6.__new__方法,__init__方法
这个内容有点多,后面单独写一篇来讲
7.__reduce__方法,__reduce_ex__方法
一句话总结:生僻的两个方法,跟序列化与反序列化有关
8.__sizeof__方法
一句话总结:简单易懂:size of object in memory, in bytes
值得一提的是:
sys模块提供的方法getsizeof(),也会返回对象的大小“Return the size of object in bytes.”,但实际使用中,使用__sizeof__()取得的值,一定会比getsizeof()要小,原因是,使用getsizeof()拿到的内存占用大小,还包含了gc时所需要的容量开销。
9.__subclasshook__方法
这个方法也单开一篇来讲
好了第一篇博客写完了,真是累啊。