# 面向對象
## 多重繼承、定制類、元類
### 動態創建類
type(classname) #類的類型就是type
因此可以看出 **類是由type函數動態創建成的**
type(classobject) # 類實例的類型是 classname 類名稱
type()函數既可以返回一個對象的類型,又**可以創建出新的類型**
>>> def fn(self, name='world'): # 先定義函數
... print('Hello, %s.' % name)
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 創建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<type 'type'>
>>> print(type(h))
<class '__main__.Hello'>
要創建一個class對象,type()函數依次傳入3個參數:
1. class的名稱;
2. 繼承的父類集合,注意Python支持多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;
3. class的方法名稱與函數綁定,這里我們把函數fn綁定到方法名hello上。
> **note:** 通過type()函數創建的類和直接寫class是完全一樣的,因為Python解釋器遇到class定義時,僅僅是掃描一下class定義的語法,然后調用type()函數創建出class。
### 控制類的創建行為 metaclass (元類) 我不要用type來創建類 我要用type的子類xxxxMetaclass
> metaclass允許你創建類或者修改類。你可以把類看成是metaclass創建出來的"實例"
> 先定義metaclass,就可以創建類,最后創建實例
# 為list添加add方法
class ListMetaclass(type):
def __new__(cls,name,bases,attrs):
# name 類名稱
# bases 繼承父類列表
# attrs 屬性/方法
attrs['add'] = lambda self,value:self.append(value) # 對類屬性/方法做出一些調整
return type.__new__(cls,name,bases,attrs)
class MyList(list): #
__metaclass__ = ListMetaclass #我不要用type來創建類 我要用type的子類
def __insert(self): # attrs 中鍵名是 '_MyList__insert'
pass
if __name__ == '__main__':
L = MyList()
L.add(1)
L.add(2)
print L # [1,2]
> **使用場景舉例**
ORM全稱“Object Relational Mapping”,即對象-關系映射,就是把關系數據庫的一行映射為一個對象,也就是一個類對應一個表,這樣,寫代碼更簡單,不用直接操作SQL語句。
要編寫一個ORM框架,所有的類都只能動態定義,因為只有使用者才能根據表的結構定義出對應的類來。
class Field(object):
def __init__(self,name,column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__,self.name)
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
def __init__(self,name):
super(IntegerField,self).__init__(name,'bigint')
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
if name == 'Model':
return type.__new__(cls,name,bases,attrs)
mappings = dict()
for k,v in attrs.iteritems():
if isinstance(v,Field):
print 'Found mapping: %s==>%s' % (k,v)
mappings[k] = v
for k in mappings.iterkeys():
attrs.pop(k)
attrs['__table__'] = name # 假設表名和類名一致
attrs['__mappings__'] = mappings # 保存屬性和列的映射關系
return type.__new__(cls,name,bases,attrs)
class Model(dict): # 繼承自 字典
__metaclass__ = ModelMetaclass
def __init__(self, **kw):
super(Model, self).__init__(**kw) #傳遞過來的參數都成了字典 鍵值
def __getattr__(self, key): # 字典本不能 dict.key 這里實現
print 'yyyyyyyyyy'
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
print 'xxxxxxxx'
self[key] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.iteritems():
fields.append(v.name)
params.append('?')
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
class User(Model):
# 定義類的屬性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
if __name__ == '__main__':
# 創建一個實例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
print u # {'email': 'test@orm.org', 'password': 'my-pwd', 'id': 12345, 'name': 'Michael'}
print u.name # yyyyyyyyyy \n Michael
# 保存到數據庫:
#u.save()
### 定制類 參見魔術方法
> \_\_getattr\_\_ 可以類的所有屬性和方法調用全部動態化處理
### python支持多重繼承
### 類方法轉換成屬性訪問 @property
class Student(object):
@property
def score(self):
return self._score
@score.setter # 不定義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 = 60 # OK,實際轉化為s.set_score(60)
>>> s.score # OK,實際轉化為s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
### 限制類能添加的屬性 \_\_slots\_\_
class Student(object):
\_\_slots\_\_ = ('name', 'age') # 試圖綁定此未定義的屬性 拋出 AttributeError異常
\_\_slots\_\_定義的屬性僅對當前類起作用
在子類中也定義\_\_slots\_\_,這樣,子類允許定義的屬性就是自身的\_\_slots\_\_加上父類的\_\_slots\_\_
### 為類動態添加方法
>>> from types import MethodType
>>> def set_score(self, score):
... self.score = score
>>> Student.set_score = MethodType(set_score, None, Student)
### 為對象動態添加方法
>>> s = Student()
>>> s.set_score = MethodType(set_score, s, Student)
## 繼承 多態
### 判斷 對象類型
type() # 類的類型就是type,實例的類型就是類名稱 (創建類就是使用type()函數)
Hello = type('Hello', (object,), dict(hello=fn)) # 創建Hello class
isinstance() #適用于對象類型、繼承關系
### 獲取對象屬性和方法
dir()
getattr() 可以獲取對象方法并變量調用
setattr()
hasattr()
### 繼承和多態
**開閉原則**
. 對擴展開放 :允許新增x子類
. 對修改封閉 : 不需要修改依賴x類型的函數