面向对象的最重要概念就是类class和实例instance,类是抽象的模板,实例是根据类创建出来的具体对象。
没有继承类的情况下,就使用object类,这是所有类最终都会继承的类。
类的__init__
方法:
由于类起到模板的作用,所以在创建实例的时候,把一些我们认为必须绑定的属性写进特殊定义的__init__
方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
其他不是必须绑定的只需要使用instance.attribute = value
来动态创建新的对象属性。
数据封装:
每个实例拥有各自的数据,我们可以封装成函数来访问这些数据:
class Student(object):
def print_score(self):
print('%s: %s' % (self.name, self.score))
定义一个方法,除了第一个参数是self以外,其他的和普通函数一样。
访问限制:
如果我们要让内部属性不被外部访问,那么我们可以在属性的名称前面加上两个下划线__
,这样它就成了一个私有变量,只有内部可以访问:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
这样就确保了外部不能直接修改对象内部的状态,但是外部依然需要访问name和score这两个属性,可以给类添加方法:
def get_name(self):
return self.__name
如果又需要外部修改score:
def set_score(self, score):
self.__score = score
有时候我们可以看到以一个下划线开头的变量名,这样的变量外部可以访问,但是意思是请不要随意访问。
继承
我们定义一个class的时候,可以从某个现有的类继承,新的类为子类subclass,被继承的类为基类,父类或者超类:
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
继承的好处是子类获得了父类的全部功能,如果重写方法,相当于覆盖了父类的方法。在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以是父类。
静态语言和动态语言:
class Timer(object):
def run(self):
print('Start...')
对于Java那样的静态语言来说,如果需要传入animal类型,则传入的对象必须是animal类型或者它的子类,否则,无法调用run()
方法。但是对于Python这样的动态语言来说,则不一定需要传入animal类型,只需要传入的对象有一个run()方法就可以了。
这就是动态语言的鸭子类型,不要求严格的继承体系,一个对象只要看起来像鸭子,走起来像鸭子,就可以被看作是鸭子。
判断对象的类型:
>>> isinstance(h, Husky)
True
深入理解元类metaclass:
在Python和大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段,但是Python中类同样也是一种对象:只要使用class关键字,解释器执行的时候就会在内存中创建一个对象,名字是ObjectCreator,这个对象(类)自身拥有创建对象的能力,这也是它是一个类的原因,但是它的本质仍然是一个对象,既然这是一个对象,那么可以:
- 可以将它赋值给一个变量
- 可以拷贝
- 可以为它增加属性!!!
- 可以作为函数的参数进行传递
动态的创建类:
>>> 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 # 这里同样得到的也是类,不是对象实例
<class '__main__'.Foo>
>>> print MyClass() # 加上括号相当于创建了类的实例,所以这里是对象
<__main__.Foo object at 0x89c6d4c>
类也是对象,所以可以动态创建,那么就在函数中创建类,像上面那样。但是这样仍然需要自己编写整个类的代码。这里就开始使用到强大的内建函数type()
了,这个函数不仅能让你知道对象的类型是什么,还能动态的创建类。
type可以接受一个类的描述作为参数,然后返回一个类:
type(类名,父类的元组(这是假设有继承关系的情况下,可以为null),包含属性的字典(名称和值))
class MyShinyClass(object): # 这个类可以通过下面的方式手动创建
pass
MyShinyClass = type('MyShinyClass', (), {}) # 返回一个类对象,这里MyShinyClass作为类名,也当做变量作为类的引用,类与变量不同。
print MyShinyClass() # 创建该类的实例
--------------
class Foo(object):
bar = True
Foo = type('Foo', (), {'bar':True}) # 接受字典来定义类的属性
-----------------
class FooChild(Foo):
pass
FooChild = type('FooChild', (Foo,),{}) # 可以向Foo继承,它的bar属性就继承自父类
-----------------
def echo_bar(self):
print self.bar
FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) # 为类增加方法,将函数作为属性赋值就可以了
到底什么是元类:
元类就是用来创建类这种对象的东西,元类就是类的类:
MyClass = MetaClass()
MyObject = MyClass()
MyClass = type('MyClass', (), {})
函数type实际上是一个元类,就是Python在背后用来创建所有类的元类,可以把元类称之为“类工厂”,type是Python的内建元类。
可以在写一个类的时候添加__metaclass__
属性:
class Foo(object): # 首先写下,但是这时候类对象还没有在内存中创建
__metaclass__ = something… # 这样会用元类来创建这个Foo类,如果有这个属性就用它创建,如果没有就用内建type创建这个类
具体分析:
class Foo(Bar):
pass
当写下如上代码的时候,Python首先看Foo中有没有metaclass属性。如果有,会在内存中通过metaclass创建一个名字为Foo的类对象。反之,它会在父类Bar中寻找metaclass属性。如果在任何父类中都找不到metaclass,它就会在模块层次中去寻找metaclass。如果最后最后都还是找不到这个属性,那么就会用内置的type来创建这个类对象。
自定义metaclass:
__metaclass__
中可以放置什么代码呢,我们需要放入用来创建类的东西。metaclass的主要用处就是当创建类的时候可以自由的改变类的结构,比如说你想根据当前情形创建符合当前上下文的类。
# 元类会自动将你通常传给‘type’的参数作为自己的参数传入,返回一个类对象,将里面的属性都转换为大写形式
def upper_attr(future_class_name, future_class_parents, future_class_attr):
# 选择所有不以'__'开头的属性
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
Python
# 将它们转为大写形式
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# 通过'type'来做类对象的创建
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # 作用到模块中的所有类
----------------------------------------------------------
class Foo(object):
# 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中
bar = 'bip'
print hasattr(Foo, 'bar')
# 输出: False
print hasattr(Foo, 'BAR')
# 输出:True
f = Foo()
print f.BAR
# 输出:'bip'
上面的例子我们使用了一个函数来当做元类,下面我们使用一个真正的class来当做元类:
# 请记住,'type'实际上是一个类,所以,你可以从type继承
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# 这里需要使用__new__方法,基本的OOP编程
return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)
上面注意到参数upperattr_metaclass,类方法的第一个参数总是代表当前的实例,就好比self,在真是的产品代码中一个metaclass如下所示:
class UpperAttrMetaclass(type):
def __new__(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return type.__new__(cls, name, bases, uppercase_attr)
# 这里我们使用super方法使之更清晰,可以从元类继承,可以从type继承
return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
总之,元类的主要用途就是创建API,比如说SQLAlchemy的ORM就是基于元类实现的。Python中的一切都是对象,要么是类的实例,要么是元类的实例。
当不希望使用元类来对类做修改的时候,可以通过其他两种技术来动态的修改类:
- 猴子补丁
- 类装饰器
99%的时间里最好使用上面两种技术来实现对类的修改。