object 是一个基类,或称之为元类。在 python2.x 上,不继承 object 类的称之为经典类,继承了 object 类的称之为新式类。
在 python3 种默认都是新式类,也即是所有的自定义类、基类都会继承object类。
object 类里面内置了许多特殊方法,这些方法的开头和结尾都是双下划线。
1. __dir__:返回一个列表,其中包含所有的属性和方法名(包含特殊方法)。函数原型如下:
def __dir__(self, *args, **kwargs):
""" Default dir() implementation. """
pass
下面举一个例子:
class Test():
pass
print(Test().__dir__()) # 也可执行 dir(obj) 来获取所有属性和方法
"""
输出如下:
['__module__', '__dict__', '__weakref__', '__doc__', '__repr__',
'__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__',
'__lt__', '__le__', '__eq__', '__ne__', '__gt__',
'__ge__', '__init__', '__new__', '__reduce_ex__', '__reduce__',
'__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
"""
2. __setattr__:当我们执行obj.属性名=属性值或setattr(obj,属性名,属性值),即为属性赋值时被调用。
class Foo(object):
def __init__(self):
self.name = 'Alex' # 打印 call __setattr__
# 重写__setattr__,为了使调用时打印输出,obj.xxx = value时调用
def __setattr__(self, key, value):
print('call __setattr__')
return object.__setattr__(self, key, value) # 调用基类的方法来赋值
f = Foo()
f.name = 'Jone' # 打印 call __setattr__
3. __getattribute__和__getattr__:使用类的对象进行obj.属性名或getattr(obj,属性名)来取对象属性的值的时候被调用,不管属性是否存在,
__getattribute__方法都会被调用。如果属性存在,则返回该属性的值,如果属性不存在,则返回None。我们在使用hasattr(obj,属性名)来判断
某个属性是否存在时,__getattribute__方法也会被调用。
与__getattr__的区别:
__getattribute__方法不管属性是否存在,都会被调用。而__getattr__只在属性不存在时调用,默认会抛出:
'AttributeError: xxx object has no attribute yyy'这样的错误,但我们可以对其进行重写,做我们需要的操作。
class Test(object):
def __init__(self):
self.name = 'Alex'
def __getattribute__(self, item):
print("call __getattribute__")
return object.__getattribute__(self, item)
def __getattr__(self, item):
print('call __getattr__')
print("%s不存在,但我可以返回一个值" % item)
return 54
f = Test()
print(f.name) # name 属性存在,打印 call __getattribute__
print(f.age) # age 属性不存在,打印 call __getattribute__,会返回None,
# 然后打印 call __getattr__, 但 __getattr__ 方法返回了54(如果没有被重写,则直接报异常),所以这里打印54。
4. __str__和__repr__:默认打印的是对象的内存地址等信息。
1)只重写__str__,则只能用于定义print(obj)时打印的内容,交互式命令行输出obj还是函数地址。
>>> class Foo(object):
... def __str__(self):
... return "我是Foo"
...
>>> f1 = Foo()
>>> print(f1)
我是Foo
>>> f1
2)只重写__repr__,则用于同时定义python命令行输出obj的内容,以及print(obj)的打印内容。
>>> class Foo(object):
... def __repr__(self):
... return "我是Foo"
...
>>> f1 = Foo()
>>> print(f1)
我是Foo
>>> f1
我是Foo
3)如果__str__和__repr__都被重写了,则__str__负责print的信息,而__repr__负责命令行直接输出obj的信息。
>>> class Foo():
... def __str__(self):
... return "我是Foo---str"
... def __repr__(self):
... return "我是Foo---repr"
...
>>> f1 = Foo()
>>> print(f1)
我是Foo---str
>>> f1
我是Foo---repr
5. __new__:__new__方法是一个静态方法,在调用时,传入你需要实例化的类名以及初始化参数列表,创建一个cls类的对象,并返回。其函数原型如下:
@staticmethod
def __new__(cls, *more):
""" Create and return a new object. See help(type) for accurate signature. """
pass
注意以下几点:
1)__new__在object被指定为@staticmethod,但更像是一个@classmethod,第一个参数传入类本身cls。
2)__new__在__init__之前运行,为传入的类(Foo)生成一个实例并返回。
3)__init__在__new__之后执行,为__new__返回的类实例进行初始化。
4)__init__是一个实例方法,是由实例来调用的。所以要执行__init__方法,必须先要由__new__生产一个实例。这就是为什么先执行__new__方法的原因。
class Foo(object):
# 后于__new__方法执行,为__new__方法生成的对象进行初始化
def __init__(self, name, age): # __new__返回的对象作为self传入__init__
print("执行__init__方法")
self.name = name
self.age = age
# __new__方法先于__init__方法执行,用于生成一个指定类的对象
def __new__(cls, *args, **kwargs): # 接收参数cls为Foo类,然后是f1 = Foo("Alex",age=32)里的name和age
print("执行__new__方法", args, kwargs)
ret = object.__new__(cls) # 调用__new__生成一个Foo对象
print(ret) # 打印 <__main__.foo object at>
return ret # 返回生成的 Foo 对象
f1 = Foo("Alex",age=32)
"""
output:
执行__new__方法 ('Alex',) {'age': 32}
执行__init__方法
"""
6. __gt__、__lt__、__ge__、__le__:这几个都是用于比较大小的,我们可以对其进行重写,来自定义对象如何比较大小。它们的原型如下:
# 判断是否大于等于 greater than or equal,在obj >= other时调用
def __ge__(self, *args, **kwargs):
""" Return self >= value. """
pass
# 判断是否大于 greater than,在obj > other时调用
def __gt__(self, *args, **kwargs):
""" Return self > value. """
pass
# 判断是否小于等于 less than or equal,在obj <= other时调用
def __le__(self, *args, **kwargs):
""" Return self <= value. """
pass
# 判断是否小于 less than,在obj
def __lt__(self, *args, **kwargs):
""" Return self < value. """
pass
下面举一个简单的例子:
class Car():
def __init__(self, price):
self.price = price
def __lt__(self, other):
print("execute __lt__")
return self.price < other.price
c1 = Car(100)
c2 = Car(200)
if c1 < c2:
print("car1 is cheaper.")
else:
print("car2 is cheaper.")
7. __eq__和__hash__:__hash__实际上是返回一个int值(计算类中所有成员的hash值),用来唯一标记这个对象。户自定义类中,如果你没有实
现__eq__和__hash__函数,那么class会继承到默认的__eq__和__hash__函数。
先来解释下什么是可hash对象:如果一个对象是可哈希的,那么在它的生存期内必须不可变(需要一个哈希函数__hash__),而且可以和其他对象比
较(需要比较方法__eq__).相同的对象一定有相同的哈希值。
1)不可hash:list, set, dict
2)可hash:数值,字符串,boolean
3)自定义类对象可不可hash:当类中只含可hash的成员时,该类定义的对象是可hash的。当我们实现的类想成为不可hash的类,则可以重写__eq__方法,
然后不重写__hash__,__hash__方法会被置None,该类的对象就不可hash了。如果不重写__eq__,则会使用基类的实现,比较对象时会比较它们的hash值。
class Foo(object):
def __init__(self):
self.name = 'Alex'
def __eq__(self, other):
return self.name == other.name
f1 = Foo()
f2 = Foo()
print(f1 == f2) # True
print(f1 is f2) # False
print(hash(f1) == hash(f2)) # 抛出异常TypeError错误,因为此时__hash__=None
8. __dict__属性:在 Python类的内部,无论是类属性还是实例属性,都是以字典的形式进行存储的,其中属性名作为键,而值作为该键对应的值。
为了方便用户查看类中包含哪些属性,Python 类提供了 __dict__ 属性。该属性可以用类名或者类的实例对象来调用。
1)每个类有自己的__dict__属性,就算存着继承关系,父类的__dict__ 并不会影响子类的__dict__,两者不共用。子类有自己的__dict__, 父类也有自
己的__dict__,子类的类属性、静态方法、普通方法、内置属性等放在子类的dict中,父类的放在父类dict中。
2)实例对象也有自己的__dict__属性, 存储 self.xxx 信息(只有属性,不包含方法),与1)不同的是父子类对象公用__dict__。
3)内置的数据类型没有__dict__属性。
我们在查找一个类属性或者对象属性的时候,也是通过__dict__字典来查的,查找顺序如下:
1)首先查找实例 obj.__dict__。
2)如果1)中未找到,则去查找类 type(obj).__dict__。
3)如果2)也未找到,则去查找父类的 __dict__,查找顺序也是先实例后类。
class Test(object):
x = 1 # 类属性
def __init__(self, y):
self.y = y # 实例属性
def fun(self):
print(self.y)
t = Test(2)
print(t.__dict__)
"""
通过实例调用__dict__,输出如下:
{'y': 2}
"""
print(Test.__dict__)
"""
因为__dict__不共用,所以输出内容不会涉及到基类object
{
'__module__': '__main__',
'x': 1,
'__init__': ,
'fun': ,
'__dict__': ,
'__weakref__': ,
'__doc__': None
}
"""
print(vars(t)) # __dict__ 也可以用 vars 函t数替代,功能完全相同
t.fun() # 在 type(t).__dict__ 中寻找,找到了直接返回
print(t.y) # 在 t.__dict__ 中寻找,找到了 y 直接返回
print(t.__dict__['y']) # 上面的调用机制实际上是这样的
print(t.x) # 在t.__dict__中找不到x,于是到type(t).__dict__中找到了x,并返回其值
print(type(t).__dict__['x']) # 上面的调用机制实际上是这样的
9. __getitem__:这个方法在 object 类里面没有被定义,因为一旦定义了这个方法,那么所有对该类对象的索引运算都会被拦截。
凡是在类中定义了这个 __getitem__ 方法,那么它的实例对象(假定为 obj),可以像这样 obj[index] 取值,当实例对象做
obj[index] 操作时,会调用类中的方法__getitem__ 方法。
class DataBase:
def __init__(self, id, address):
self.id = id
self.address = address
self.d = {
self.id: 1,
self.address: "192.168.1.1"
}
def __getitem__(self, key):
return self.d.get(key, "default")
data = DataBase(1, "192.168.2.11")
print(data["hi"])
print(data[data.id])
"""
default
1
"""
10. __len__:如果一个类表现得像一个 list,要获得有多少个元素,就得用 len();要让 len() 函数正常工作,类必须提供一个特
殊的方法__len__(),才能返回元素的个数。
class Fib:
def __init__(self, num):
a, b, L = 0, 1, []
for i in range(num):
L.append(a)
a, b = b, a+ b
self.numbers = L
def __str__(self):
return str(self.numbers)
def __len__(self):
return len(self.numbers)
f = Fib(10)
print(f)
print(len(f))
"""
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
10
"""