1 内建函数(__xxx__)
背景:为什么要有这种带下划线的内建函数,个人认为这种内建函数开放了很多Python的特殊用法,只要详尽掌握,就会理解平时用到的数据结构是复写了什么方法,自己也可以写出类似set,dqueue,dict,list的数据类型方法。
1.1 类似字典的操作方式,__getitem__,__setitem__,__delitem__
我们熟悉的字典是可以像如下方式操作赋值取值:
d = dict()
d["a"] = 1
d["b"] = 2
print d
del d["a"]
print d
其实像这种赋值方式就是复写了__setitem__,__getitem__,__delitem__请见如下类
class TestDict(object):
def __getitem__(self,key):
return self.__dict__.get(key)
def __setitem__(self,key,value):
self.__dict__[key] = value
def __delitem__(self,key):
self.__dict__.pop(key)
td = TestDict()
td["a"] = 1
td["b"] = 2
print td["a"]
print td.__dict__
del td["a"]
print td.__dict__
为了更加灵活,如上代码也可以在对象执行赋值方法(__init__)的时候声明个变量d = dict(),然后对这个变量进行赋值和取值操作也可以模拟上面的操作。
1.2 __new__和__init__
__new__: 在类实例化的时候调用,用来创建实例,如果不返回实例那么__init__将不会执行,第一个参数是class对象,在创建实例的时候需要有返回值
__init__: 在初始化实例的时候调用,比如说实例属性赋值,第一个参数是实例对象,一般都重写__init__方法,在执行的时候不需要返回值
class TestNew(object):
def __new__(cls, *args, **kwargs):
print '__new__ called.'
return super(TestNew,cls).__new__(cls,*args,**kwargs)
def __init__(self):
print '__init__ called.'
self.a = 1
tn = TestNew()
print tn.a
可以用__new__来实现单例模式
class Singleton(object):
def __new__(cls):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
obj1 = Singleton()
obj2 = Singleton()
obj1.attr1 = 'value1'
print obj1.attr1, obj2.attr1
print obj1 is obj2
Tips: 单例模式有很多种实现方式,也可以通过类变量+静态方法的方式实现。
可以通过重载__new__来实现很多创建实例时的功能。
1.3 __iter__,next(python2),__next__(python3)
__iter__: 复写这个函数的对象是可迭代对象
next/__next__: 复写这个函数的对象都是一个迭代器
class TestIterNext(object):
def __init__(self,data=1):
self.data = data
def next(self):
if self.data > 5:
raise StopIteration
else:
self.data+=1
return self.data
def __iter__(self):
print "iter"
return self
当for循环去迭代tin = TestIterNext()对象的时候第一步会去看__iter__是否返回一个生成器(generator),如果返回的是对象本身才会去执行next函数。
1.4 __call__
把一个类实例的对象当做函数一样调用就是复写了__call__方法,如下:
class TestCall(object):
def __call__(self):
print "call it"
tc = TestCall()
tc()
复写__call__方法大多数用在装饰器类中(第5章)和继承Type类(第3章)的时候。
1.5 __repr__,__str__
__repr__和__str__没有太大区别,在Python交互模式下才能发现。复写改方法后输出对象就是按照该方法里的内容进行输出。在类里可以这样用__repr__ = __str__
1.6 __all__
可用于模块导入时限制,,当我们from module import *的时候这个__all__就起作用了,__all__=["bar","sar"] ,[]里定义函数或者变量类等,有些模块内部一些函数不对外开放,此时把一些对外开放的函数变量放入到__all__里就可以了,这样避免了一些多余的导入。如果在__init__.py里定义则在导入模块的时候只导入__all__里定义的各个文件,此时无法定义到文件里具体哪个类或方法,如果需要细化则需要在具体的类里写入__all__。如果是from module import Test这种使用不受__all__限制
1.7 __setattr__,__getattr__,__delattr__
__setattr__: 对变量赋值时调用。
__getattr__:默认查找对象属性是在一个字典里(__dict__),这里没有要查找的对象则去__getattr__方法里查找,如果我们复写__getattr__则可以根据实际需求来返回值。
__delattr__:删除属性时调用。
class TestAttr(object):
def __init__(self):
self.name = "abc"
def __getattr__(self, item):
print "item:" + str(item)
print "getattr"
return 10
def __setattr__(self, *args, **kwargs):
print "set attr"
object.__setattr__(self,*args,**kwargs)
def __delattr__(self, *args, **kwargs):
print "delete attr"
object.__delattr__(self,*args, **kwargs)
ta = TestAttr()
print ta.__dict__
print ta.names
del ta.name
print ta.__dict__
1.8 __le__,__lt__,__ge__,__gt__,__ne__,__eq__
一个对象和另一个对象比较大小,返回的并不一定是True和False,返回值有可能是我们定义的任何值,这里就是复写上面这些方法。如下:
class TestCompare(object):
def __lt__(self, other):
return "aaa"
t = TestCompare()
print t<1
这里打印出的就不是True和False,打印的是我们再复写的方法里定义的"aaa",其实有一些常用的orm,比如说sqlalchemy里查询条件可以这样写
g.pg_db.query(Company.id).filter(Company.level_id == level)
这里的Company.level_id == level返回的就是筛选条件,因为对Company的level_id对象复写了__eq__
1.9 __slots__
优点:
1,更快的属性访问速度
2,减少内存消耗
每个类里都维护一个字典__dict__,这个字典维护了对象的所有属性,但如果成千上万个对象则就会创建很多个__dict__来存放对象属性,为了性能我们可以不用Python帮我们维护这个字典。此时我们在类里定义__slots__ = ["name","age"]的时候就表示禁用了__dict__,并限定name和age为类的属性(类里只能有name和age属性),这样做的好处能大大节省内存开支,对象越多节省的就越多,大概能节省40%以上。
class TestSlots(object):
__slots__ = ["name","age"]
def __init__(self, name, age):
self.name = name
self.age = age
ts = TestSlots("a",1)
ts.name = 1
print ts.name
2.0 __metaclass__
在理解元类之前,你需要先掌握Python中的类。Python中类的概念借鉴于Smalltalk,这显得有些奇特。在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立
元类是什么?
但就元类本身而言,它们其实是很简单的:
1) 拦截类的创建
2) 修改类
3) 返回修改之后的类
但是,Python中的类还远不止如此。类同样也是一种对象。是的,没错,就是对象。只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。下面的代码段:
class ObjectCreator(object):
pass
将在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:
1) 你可以将它赋值给一个变量
2) 你可以拷贝它
3) 你可以为它增加属性
4) 你可以将它作为函数参数进行传递
下面是示例:
print ObjectCreator
# 你可以打印一个类,因为它其实也是一个对象
def echo(o):
print o
echo(ObjectCreator)
# 你可以将类做为参数传给函数
print hasattr(ObjectCreator, 'new_attribute')
ObjectCreator.new_attribute = 'foo'
# 你可以为类增加属性
print hasattr(ObjectCreator, 'new_attribute')
print ObjectCreator.new_attribute
ObjectCreatorMirror = ObjectCreator
# 你可以将类赋值给一个变量
print ObjectCreatorMirror()
动态地创建类
因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用class关键字即可。
def choose_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo
# 返回的是类,不是类的实例
else:
class Bar(object):
pass
return Bar
MyClass = choose_class('foo')
print MyClass
# 函数返回的是类,不是类的实例>>> print MyClass()
# 你可以通过这个类创建类实例,也就是对象
# <__main__.foo object="" at="" 0x89c6d4c="">
但这还不够动态,因为你仍然需要自己编写整个类的代码。由于类也是对象,所以它们必须是通过什么东西来生成的才对。当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。还记得内建函数type吗?这个古老但强大的函数能够让你知道一个对象的类型是什么,就像这样:
print type(1)
print type("1")
print type(ObjectCreator)
print type(ObjectCreator())
这里,type有一种完全不同的能力,它也能动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。(我知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)
type可以像这样工作:
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
比如下面的代码:
class MyShinyClass(object):
pass
MyShinyClass = type('MyShinyClass', (), {})
# 返回一个类对象
print MyShinyClass
print MyShinyClass()
# 创建一个该类的实例
# <__main__.myshinyclass object="" at="" 0x8997cec="">
你会发现我们使用“MyShinyClass”作为类名,并且也可以把它当做一个变量来作为类的引用。类和变量是不同的,这里没有任何理由把事情弄的复杂。
type 接受一个字典来为类定义属性,因此
class Foo(object):
bar = True
# 可以翻译为:
Foo = type('Foo', (), {'bar':True})
为类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。
def echo_bar(self):
print self.bar
FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
hasattr(Foo, 'echo_bar')
False
hasattr(FooChild, 'echo_bar')
True
my_foo = FooChild()
my_foo.echo_bar()
True
你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。
到底什么是元类
元类就是用来创建类的“东西”。你创建类就是为了创