Python高级特性(1):Iterators、Generators和itertools
Python高级特性(2):Closures、Decorators和functools
类和对象
类和函数一样都是Python中的对象。当一个类定义完成之后,Python将创建一个“类对象”并将其赋值给一个同名变量。类是type类型的对象(是不是有点拗口?)。
类对象是可调用的(callable,实现了 __call__方法),并且调用它能够创建类的对象。你可以将类当做其他对象那么处理。例如,你能够给它们的属性赋值,你能够将它们赋值给一个变量,你可以在任何可调用对象能够用的地方使用它们,比如在一个map中。事实上当你在使用map(str, [1,2,3])的时候,是将一个整数类型的list转换为字符串类型的list,因为str是一个类。可以看看下面的代码:
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 |
>>> class C(object): ... def __init__(self, s): ... print s ... >>> myclass = C >>> type(C) <type 'type'> >>> type(myclass) <type 'type'> >>> myclass(2) 2 <__main__.C object at 0x10e2bea50> >>> map(myclass, [1,2,3]) 1 2 3 [<__main__.C object at 0x10e2be9d0>, <__main__.C object at 0x10e2bead0>, <__main__.C object at 0x10e2beb10>] >>> map(C, [1,2,3]) 1 2 3 [<__main__.C object at 0x10e2be950>, <__main__.C object at 0x10e2beb50>, <__main__.C object at 0x10e2beb90>] >>> C.test_attribute = True >>> myclass.test_attribute True |
正因如此,Python中的“class”关键字不像其他语言(例如C++)那样必须出现在代码main scope中。在Python中,它能够在一个函数中嵌套出现,举个例子,我们能够这样在函数运行的过程中动态的创建类。看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
>>> def make_class(class_name): ... class C(object): ... def print_class_name(self): ... print class_name ... C.__name__ = class_name ... return C ... >>> C1, C2 = map(make_class, ["C1", "C2"]) >>> c1, c2 = C1(), C2() >>> c1.print_class_name() C1 >>> c2.print_class_name() C2 >>> type(c1) <class '__main__.C1'> >>> type(c2) <class '__main__.C2'> >>> c1.print_class_name.__closure__ (<cell at 0x10ab6dbe8: str object at 0x10ab71530>,) |
请注意,在这里通过make_class创建的两个类是不同的对象,因此通过它们创建的对象就不属于同一个类型。正如我们在装饰器中做的那样,我们在类被创建之后手动设置了类名。同样也请注意所创建类的print_class_name方法在一个closure cell中捕捉到了类的closure和class_name。如果你对closure的概念还不是很清楚,那么最好去看看前篇,复习一下closures和decorators相关的内容。
Metaclasses
如果类是能够制造对象的对象,那制造类的对象又该叫做什么呢(相信我,这并不是一个先有鸡还是先有蛋的问题)?答案是元类(Metaclasses)。大部分常见的基础元类都是type。当输入一个参数时,type将简单的返回输入对象的类型,这就不涉及元类。然而当输入三个参数时,type将扮演元类的角色,基于输入参数创建一个类并返回。输入参数相当简单:类名,父类及其参数的字典。后面两者可以为空,来看一个例子:
1 2 3 4 5 6 |
>>> MyClass = type("MyClass", (object,), {"my_attribute": 0}) >>> type(MyClass) <type 'type'> >>> o = MyClass() >>> o.my_attribute 0 |
特别注意第二个参数是一个tuple(语法看起来很奇怪,以逗号结尾)。如果你需要在类中安排一个方法,那么创建一个函数并且将其以属性的方式传递作为第三个参数,像这样:
1 2 3 4 5 6 7 8 9 |
>>> def myclass_init(self, my_attr): ... self.my_attribute = my_attr ... >>> MyClass = type("MyClass", (object,), {"my_attribute": 0, "__init__": myclass_init}) >>> o = MyClass("Test") >>> o.my_attribute 'Test' >>> o.__init__ <bound method MyClass.myclass_init of <__main__.MyClass object at 0x10ab72150>> |
我们可以通过一个可调用对象(函数或是类)来自定义元类,这个对象需要三个输入参数并返回一个对象。这样一个元类在一个类上实现只要定义了它的__metaclass__属性。第一个例子,让我们做一些有趣的事情看看我们能够用元类做些什么:
1 2 3 4 5 6 7 8 9 10 |
>>> def mymetaclass(name, parents, attributes): ... return "Hello" ... >>> class C(object): ... __metaclass__ = mymetaclass ... >>> print C Hello >>> type(C) <type 'str'> |
请注意以上的代码,C只是简单地将一个变量引用指向了字符串“Hello”。当然了,没人会在实际中写这样的代码,这只是为了演示元类的用法而举的一个简单例子。接下来我们来做一些更有用的操作。在本系列的第二部分我们曾看到如何使用装饰器类来记录目标类每个方法的输出,现在我们来做同样的事情,不过这一次我们使用元类。我们借用之前的装饰器定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
def log_everything_metaclass(class_name, parents, attributes): print "Creating class", class_name myattributes = {} for name, attr in attributes.items(): myattributes[name] = attr |