前提
new、init、call的介紹
在講到使用元類創建單例模式之前,比需了解new這個內置方法的作用,在上面講元類的時候我們用到了new方法來實現類的創建。然而我在那之前還是對new這個方法和init方法有一定的疑惑。因此這里花點時間對其概念做一次了解和區分。
new
該方法負責創建一個實例對象,在對象被創建的時候調用該方法它是一個類方法。new方法在返回一個實例之后,會自動的調用init方法,對實例進行初始化。如果new方法不返回值,或者返回的不是實例,那么它就不會自動的去調用init方法。
init
該方法負責將該實例對象進行初始化,在對象被創建之后調用該方法,在new方法創建出一個實例后對實例屬性進行初始化。init方法可以沒有返回值。
call
該方法其實和類的創建過程和實例化沒有多大關系了,定義了call方法才能被使用函數的方式執行。
#coding:utf-8
class Foo(object):
def __new__(cls, *args, **kwargs):
#__new__是一個類方法,在對象創建的時候調用
print "excute __new__"
return super(Foo,cls).__new__(cls,*args,**kwargs)
def __init__(self,value):
#__init__是一個實例方法,在對象創建后調用,對實例屬性做初始化
print "excute __init"
self.value = value
f1 = Foo(1)
print f1.value
f2 = Foo(2)
print f2.value
#輸出===:
excute __new__
excute __init
excute __new__
excute __init
#====可以看出new方法在init方法之前執行
type
type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
元類的作用是產生類實例
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 0x1085ed950
在Python中,類也是對象,你可以動態的創建類。這就是當我們使用關鍵字class時Python在幕后做的事情,而這就是通過元類來實現的
元類
什么是元類
通過上文的描述,我們知道了Python中的類也是對象。元類就是用來創建這些類(對象)的,元類就是類的類,你可以這樣理解為:
MyClass = MetaClass() #元類創建
MyObject = MyClass() #類創建實例
實際上MyClass就是通過type()來創創建出MyClass類,它是type()類的一個實例;同時MyClass本身也是類,也可以創建出自己的實例,這里就是MyObject
metaclass屬性
你可以在寫一個類的時候為其添加metaclass屬性,定義了metaclass就定義了這個類的元類。
class Foo(object): #py2
__metaclass__ = something…
class Foo(metaclass=something): #py3
__metaclass__ = something…
例如:當我們寫如下代碼時 :
class Foo(Bar):
pass
在該類并定義的時候,它還沒有在內存中生成,知道它被調用。Python做了如下的操作:
1)Foo中有metaclass這個屬性嗎?如果是,Python會在內存中通過metaclass創建一個名字為Foo的類對象(我說的是類對象,請緊跟我的思路)。
2)如果Python沒有找到metaclass,它會繼續在父類中尋找metaclass屬性,并嘗試做和前面同樣的操作。
3)如果Python在任何父類中都找不到metaclass,它就會在模塊層次中去尋找metaclass,并嘗試做同樣的操作。
4)如果還是找不到metaclass,Python就會用內置的type來創建這個類對象。
現在的問題就是,你可以在metaclass中放置些什么代碼呢?
答案就是:可以創建一個類的東西。那么什么可以用來創建一個類呢?type,或者任何使用到type或者子類化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('__'))
# 將它們轉為大寫形式
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
#通過'type'來做類對象的創建
return type(future_class_name, future_class_parents, uppercase_attr)#返回一個類
class Foo(object):
__metaclass__ = upper_attr
bar = 'bip'
使用class來當做元類
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 super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
orm
#coding:utf-8
#一、首先來定義Field類,它負責保存數據庫表的字段名和字段類型:
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')
#二、定義元類,控制Model對象的創建
class ModelMetaclass(type):
'''定義元類, __new__必須要有返回值,返回實例化出來的實例,此處返回Model類實例'''
def __new__(cls, name, bases, attrs):
if name=='Model':
# 實例化出<class '__main__.Model'>
# super(ModelMetaclass,cls) 等于type
'''
__new__()方法接收到的參數依次是:
1、當前準備創建的類的對象;
2、類的名字;
3、類繼承的父類集合;
4、類的方法或者屬性集合。
'''
cc = super(ModelMetaclass,cls).__new__(cls, name, bases, attrs)
return cc
mappings = dict()
for k, v in attrs.iteritems():
# 保存類屬性和列的映射關系到mappings字典
if isinstance(v, Field):
print('Found mapping: %s==>%s' % (k, v))
mappings[k] = v
for k in mappings.iterkeys():
#將類屬性移除,使定義的類字段不污染User類屬性,只在實例中可以訪問這些key
attrs.pop(k)
attrs['__table__'] = name.lower() # 假設表名和為類名的小寫,創建類時添加一個__table__類屬性
attrs['__mappings__'] = mappings # 保存屬性和列的映射關系,創建類時添加一個__mappings__類屬性
dd = super(ModelMetaclass,cls).__new__(cls, name, bases, attrs)
return dd
#三、編寫Model基類
class Model(dict):
__metaclass__ = ModelMetaclass
def __init__(self, **kw):
super(Model, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
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))
#最后,我們使用定義好的ORM接口,使用起來非常的簡單。
class User(Model):
# 定義類的屬性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 創建一個實例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到數據庫:
u.save()
元類是生成類的類 所以都放在初始化new函數中,讓生成類更加靈活。
元類單例模式
new方法實現單例
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls,"_instance"):
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
s1 = Singleton()
s2 = Singleton()
print s1 is s2
元類實現單例
class Singleton(type):
def __init__(self, *args, **kwargs):
print "__init__"
self.__instance = None
super(Singleton,self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
print "__call__"
if self.__instance is None:
self.__instance = super(Singleton,self).__call__(*args, **kwargs)
return self.__instance
class Foo(object):
__metaclass__ = Singleton #在代碼執行到這里的時候,元類中的__new__方法和__init__方法其實已經被執行了,而不是在Foo實例化的時候執行。且僅會執行一次。
foo1 = Foo()
foo2 = Foo()
print Foo.__dict__ #_Singleton__instance': <__main__.Foo object at 0x100c52f10> 存在一個私有屬性來保存屬性,而不會污染Foo類(其實還是會污染,只是無法直接通過__instance屬性訪問)
print foo1 is foo2 # True