面向对象高级编程
2016-06-15 23:03
330 查看
面向对象高级编程
使用__slots__
当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性class Student(object): pass
尝试给实例绑定一个属性:
class Student(object): pass
s = Student()
s.name = "csx"
print s.name
# 输出:csx
还可以尝试给实例绑定一个方法:
# coding=utf-8
class Student(object): pass
s = Student()
s.name = "csx"
print s.name
# 定义一个方法作为实例方法
def set_age(self, age):
self.age = age
from types import MethodType
# 给实例绑定一个方法
s.set_age = MethodType(set_age, s, Student)
s.set_age(25)
print s.age
但是,给一个实例绑定的方法,对另一个实例是不起作用的:
为了给所有实例都绑定方法,可以给class绑定方法:
Student.set_score = MethodType(set_score, None, Student)
使用__slots__
如果我们想要限制class的属性怎么办?比如,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class能添加的属性:
# coding=utf-8 class student(object): __slots__ = ("name", "age") s = student() s.name = "csx" s.age = 13 s.score = 90 """ Traceback (most recent call last): File "G:/python/day2/slot.py", line 9, in <module> s.score = 90 AttributeError: 'student' object has no attribute 'score' """
使用@property
封装属性时,同时进行参数检查# coding=utf-8 class student(object): def get_score(self): return self._score def set_score(self,score): if not isinstance(score,int): raise ValueError('score must be an integer!') if score < 0 or score > 100: raise ValueError('score must between 0 ~ 100!') self._score = score s = student() s.set_score(70) print s.get_score() # 输出:70 s.set_score("csx") print s.get_score() """ Traceback (most recent call last): File "G:/python/day2/slot.py", line 16, in <module> s.set_score("csx") File "G:/python/day2/slot.py", line 7, in set_score raise ValueError('score must be an integer!') ValueError: score must be an integer! """
上面的调用方法又略显复杂,没有直接用属性这么直接简单。
还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的:
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value s = Student() s.score = 90 print s.score # 输出:90
还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:
多重继承
实例
class Mammal(Animal): pass class Runnable(object): def run(self): print('Running...') class Flyable(object): def fly(self): print('Flying...') # 多重继承 class Dog(Mammal, Runnable): pass
Mixin
在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为Mixin。Mixin的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系。
定制类
__str__
类似java中的toString,输出默认的打印信息__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。
__iter__
如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def next(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration(); return self.a # 返回下一个值
__getitem__
像list那样按照下标取出元素,需要实现__getitem__()方法:class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a
__getattr__
解决,调用不存在属性时,调用出错的问题class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99 s = Student() s.score # 输出:99
当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,
__call__
一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?类似instance()?在Python中,答案是肯定的。任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。请看示例:
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name) s = Student('Michael') s() # 输出:My name is Michael.
call()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。
使用元类
type()动态创建类
type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:def fn(self, name='world'): # 先定义函数 print('Hello, %s.' % name) Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
要创建一个class对象,type()函数依次传入3个参数:
class的名称;
继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。
我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:
定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:
# metaclass是创建类,所以必须从`type`类型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) class MyList(list): __metaclass__ = ListMetaclass # 指示使用ListMetaclass来定制类
当我们写下__metaclass__ = ListMetaclass语句时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.new()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
__new__()方法接收到的参数依次是:
当前准备创建的类的对象;
类的名字;
类继承的父类集合;
类的方法集合。
相关文章推荐
- C#中Property和Attribute的区别实例详解
- JavaScript中检查对象property的存在性方法介绍
- JavaScript中对象property的删除方法介绍
- JavaScript中遍历对象的property的3种方法介绍
- javascript 对象属性property与元素属性attribute的浏览器支持
- Lua多重继承代码实例
- JavaScript Table排序 2.0 (更新)
- JavaScript对象的property属性详解
- Lua面向对象之多重继承、私密性详解
- JavaScript中property和attribute的区别详细介绍
- JavaScript中的property和attribute介绍
- JavaScript从数组的indexOf()深入之Object的Property机制
- C++ 多重继承和虚拟继承对象模型、效率分析
- javascript中attribute和property的区别详解
- JavaScript中对象property的读取和写入方法介绍
- python中实现定制类的特殊方法总结
- python中的多重继承实例讲解
- Python Property属性的2种用法
- python多重继承新算法C3介绍
- Python中用Descriptor实现类级属性(Property)详解