# -*- encoding: utf-8 -*-
import os
'''
第35條: 用元類來註解類的屬性
關鍵:
1 元類
作用:在類定義好但是還沒有使用的時候,提前修改或註解類的屬性,
該寫法通常會與描述符搭配起來,使這些屬性可以瞭解自己在外圍類的使用方式
應用場景:
定義某個類,表示客戶數據庫中的某一行。還希望該類的相關屬性與數據庫表
的沒一列之間,建立對應關係
2 描述符
描述符類: 可以提供__get__和__set__方法
原理:
exam.writingGrade = 40
會被轉換爲
Exam.__dict__['writingGrade'].__set__(exam, 40)
獲取屬性會被轉換爲
Exam.__dict__['writingGrade'].__get__(exam, Exam)
之所以存在上述轉換: 是因爲object類的__getattribute__方法
類實例沒有某個屬性 ---> 在類中查找同名屬性(
類屬性,如果實現了__get__和__set__方法,則認爲該對象遵循描述符協議)
3 總結
元類可以在類完全定義好之前,修改該類的屬性;
描述符與元類組合後,可以對行爲作出修飾,不使用weakref就避免內存泄露
參考:
Effectiv Python 編寫高質量Python代碼的59個有效方法
'''
'''
描述符類: 把屬性與列名聯繫起來。
列的名稱已經保存到了Field描述符中。
'''
class Field2(object):
def __init__(self, name):
self.name = name
self.internalName = "_" + self.name
def __get__(self, instance, instance_type):
if instance is None:
return self
return getattr(instance, self.internalName, '')
def __set__(self, instance, value):
setattr(instance, self.internalName, value)
class Customer(object):
# Class attributes
first_name = Field2('first_name')
last_name = Field2('last_name')
prefix = Field2('prefix')
suffix = Field2('suffix')
def useCustomer2():
customer = Customer()
print "Before: {value}, {mydict}".format(
value=repr(customer.first_name),
mydict=customer.__dict__
)
customer.first_name = 'Chen'
print "After: {value}, {mydict}".format(
value=repr(customer.first_name),
mydict=customer.__dict__
)
class Field(object):
def __init__(self):
# 下面的屬性已經被元類設置了
self.name = None
self.internalName = None
def __get__(self, instance, instance_type):
if instance is None:
return self
return getattr(instance, self.internalName, '')
def __set__(self, instance, value):
return setattr(instance, self.internalName, value)
class Meta(type):
def __new__(meta, name, bases, class_dict):
for key, value in class_dict.items():
if isinstance(value, Field):
value.name = key
value.internalName = "_" + key
cls = type.__new__(meta, name, bases, class_dict)
return cls
class DatabaseRow(object):
__metaclass__ = Meta
class BetterCustomer(DatabaseRow):
first_name = Field()
last_name = Field()
prefix = Field()
suffix = Field()
def useCustomer():
customer = Customer()
print "Before: {value}, {mydict}".format(
value=repr(customer.first_name),
mydict=customer.__dict__
)
customer.first_name = 'Chen'
print "After: {value}, {mydict}".format(
value=repr(customer.first_name),
mydict=customer.__dict__
)
def process():
useCustomer()
if __name__ == "__main__":
process()