网站超市源码哪个好,大港网站建设公司,数据分析网站,聊天室网站模板https://www.cnblogs.com/tkqasn/p/6524879.html 在看一些框架源代码的过程中碰到很多元类的实例#xff0c;看起来很吃力很晦涩#xff1b;在看python cookbook中关于元类创建单例模式的那一节有些疑惑。因此花了几天时间研究下元类这个概念。通过学习元类#xff0c;我对p…https://www.cnblogs.com/tkqasn/p/6524879.html 在看一些框架源代码的过程中碰到很多元类的实例看起来很吃力很晦涩在看python cookbook中关于元类创建单例模式的那一节有些疑惑。因此花了几天时间研究下元类这个概念。通过学习元类我对python的面向对象有了更加深入的了解。这里将一篇写的非常好的文章基本照搬过来吧这是一篇在Stack overflow上很热的帖子我看http://blog.jobbole.com/21351/这篇博客对其进行了翻译。
一、理解类也是对象
在理解元类之前你需要先掌握Python中的类。Python中类的概念借鉴于Smalltalk这显得有些奇特。在大多数编程语言中类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立
class ObjectCreator(object):passmy_object ObjectCreator()
print my_object
输出
__main__.ObjectCreator object at 0x8974f2c但是Python中的类还远不止如此。类同样也是一种对象。只要你使用关键字classPython解释器在执行的时候就会创建一个对象。下面的代码段
class ObjectCreator(object):pass将在内存中创建一个对象名字就是ObjectCreator。这个对象类自身拥有创建对象类实例的能力而这就是为什么它是一个类的原因。但是它的本质仍然是一个对象于是你可以对它做如下的操作 你可以将它赋值给一个变量 你可以拷贝它 你可以为它增加属性 你可以将它作为函数参数进行传递。
下面是示例
print ObjectCreator # 你可以打印一个类因为它其实也是一个对象
输出class __main__.ObjectCreatordef echo(o):print oecho(ObjectCreator) # 你可以将类做为参数传给函数
输出class __main__.ObjectCreator
print hasattr(ObjectCreator, new_attribute)
输出FalseObjectCreator.new_attribute foo # 你可以为类增加属性
print hasattr(ObjectCreator, new_attribute)
输出True
print ObjectCreator.new_attribute
输出fooObjectCreatorMirror ObjectCreator # 你可以将类赋值给一个变量
print ObjectCreatorMirror()
输出__main__.ObjectCreator object at 0x108551310二、动态地创建类
1、通过return class动态的构建需要的类
因为类也是对象你可以在运行时动态的创建它们就像其他任何对象一样。首先你可以在函数中创建类使用class关键字即可。
def choose_class(name):if name foo:class Foo(object):passreturn Foo # 返回的是类不是类的实例else:class Bar(object):passreturn Bar
MyClass choose_class(foo)print MyClass # 函数返回的是类不是类的实例
输出class __main__.Fooprint MyClass() # 你可以通过这个类创建类实例也就是对象
输出__main__.Foo object at 0x1085ed9502、通过type函数构造类
但这还不够动态因为你仍然需要自己编写整个类的代码。由于类也是对象所以它们必须是通过什么东西来生成的才对。当你使用class关键字时Python解释器自动创建这个对象。但就和Python中的大多数事情一样Python仍然提供给你手动处理的方法。还记得内建函数type吗这个古老但强大的函数能够让你知道一个对象的类型是什么就像这样
print type(1)
输出type int
print type(1)
输出type str
print type(ObjectCreator)
输出type type
print type(ObjectCreator())
输出class __main__.ObjectCreator这里type有一种完全不同的能力它也能动态的创建类。type可以接受一个类的描述作为参数然后返回一个类。我知道根据传入参数的不同同一个函数拥有两种完全不同的用法是一件很傻的事情但这在Python中是为了保持向后兼容性
type的语法 type(类名, 父类的元组针对继承的情况可以为空包含属性的字典名称和值) 比如下面的代码 class MyShinyClass(object):pass可以手动通过type创建其实
MyShinyClass type(MyShinyClass, (), {}) # 返回一个类对象
print MyShinyClass
输出class __main__.MyShinyClass
print MyShinyClass() # 创建一个该类的实例
输出__main__.MyShinyClass object at 0x1085cd810你会发现我们使用“MyShinyClass”作为类名并且也可以把它当做一个变量来作为类的引用。
接下来我们通过一个具体的例子看看type是如何创建类的范例
1.构建Foo类
#构建目标代码
class Foo(object):bar True
#使用type构建
Foo type(Foo, (), {bar:True})2.继承Foo类
#构建目标代码
class FooChild(Foo):pass
#使用type构建
FooChild type(FooChild, (Foo,),{})print FooChild
输出class __main__.FooChild
print FooChild.bar # bar属性是由Foo继承而来
输出True3.为Foochild类增加方法
def echo_bar(self):print self.barFooChild type(FooChild, (Foo,), {echo_bar: echo_bar})
hasattr(Foo, echo_bar)
输出False
hasattr(FooChild, echo_bar)
输出True
my_foo FooChild()
my_foo.echo_bar()
输出True可以看到在Python中类也是对象你可以动态的创建类。这就是当我们使用关键字class时Python在幕后做的事情而这就是通过元类来实现的。
三、元类
1、什么是元类
通过上文的描述我们知道了Python中的类也是对象。元类就是用来创建这些类对象的元类就是类的类你可以这样理解为
MyClass MetaClass() #元类创建
MyObject MyClass() #类创建实例实际上MyClass就是通过type()来创创建出MyClass类它是type()类的一个实例同时MyClass本身也是类也可以创建出自己的实例这里就是MyObject 函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢好吧我猜这是为了和str保持一致性str是用来创建字符串对象的类而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西注意我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象而且它们都是从一个类创建而来。
age 35
age.__class__
输出type intname bob
name.__class__
输出type strdef foo(): pass
foo.__class__
输出type functionclass Bar(object): pass
b Bar()
b.__class__
输出class __main__.Bar对于任何一个__class__的__class__属性又是什么呢
a.__class__.__class__
输出type type
age.__class__.__class__
输出type type
foo.__class__.__class__
输出type type
b.__class__.__class__
输出type type因此元类就是创建类这种对象的东西 type就是Python的内建元类当然了你也可以创建自己的元类。
2、__metaclass__属性
你可以在写一个类的时候为其添加__metaclass__属性,定义了__metaclass__就定义了这个类的元类。
class Foo(object): #py2__metaclass__ something…class Foo(metaclasssomething): #py3__metaclass__ something…例如当我们写如下代码时 :
class Foo(Bar):pass在该类并定义的时候它还没有在内存中生成知道它被调用。Python做了如下的操作
1Foo中有__metaclass__这个属性吗如果是Python会在内存中通过__metaclass__创建一个名字为Foo的类对象我说的是类对象请紧跟我的思路。2如果Python没有找到__metaclass__它会继续在父类中寻找__metaclass__属性并尝试做和前面同样的操作。3如果Python在任何父类中都找不到__metaclass__它就会在模块层次中去寻找__metaclass__并尝试做同样的操作。4如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
现在的问题就是你可以在__metaclass__中放置些什么代码呢 答案就是可以创建一个类的东西。那么什么可以用来创建一个类呢type或者任何使用到type或者子类化type的东西都可以。
三、自定义元类
元类的主要目的就是为了当创建类时能够自动地改变类。通常你会为API做这样的事情你希望可以创建符合当前上下文的类。假想一个很傻的例子你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到但其中一种就是通过设定__metaclass__。采用这种方法这个模块中的所有类都会通过这个元类来创建我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。
__metaclass__实际上可以被任意调用它并不需要是一个正式的类。所以我们这里就先以一个简单的函数作为例子开始。
1、使用函数当做元类
# 元类会自动将你通常传给‘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_attrbar bip print hasattr(Foo, bar)
输出: False
print hasattr(Foo, BAR)
输出:Truef Foo()
print f.BAR
输出:bip2、使用class来当做元类
由于__metaclass__必须返回一个类。
- 请记住type实际上是一个类就像str和int一样。所以你可以从type继承
- __new__ 是在__init__之前被调用的特殊方法__new__是用来创建对象并返回之的方法__new_()是一个类方法
- 而__init__只是用来将传入的参数初始化给对象它是在对象创建之后执行的方法。
- 你很少用到__new__除非你希望能够控制对象的创建。这里创建的对象是类我们希望能够自定义它所以我们这里改写__new__
- 如果你希望的话你也可以在__init__中做些事情。还有一些高级的用法会涉及到改写__call__特殊方法但是我们这里不用下面我们可以单独的讨论这个使用class UpperAttrMetaClass(type):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)return type(future_class_name, future_class_parents, uppercase_attr)#返回一个对象但同时这个对象是一个类但是这种方式其实不是OOP。我们直接调用了type而且我们没有改写父类的__new__方法。现在让我们这样去处理:
class UpperAttrMetaclass(type):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)# 复用type.__new__方法# 这就是基本的OOP编程没什么魔法。由于type是元类也就是类因此它本身也是通过__new__方法生成其实例只不过这个实例是一个类.return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)你可能已经注意到了有个额外的参数upperattr_metaclass这并没有什么特别的。类方法的第一个参数总是表示当前的实例就像在普通的类方法中的self参数一样。当然了为了清晰起见这里的名字我起的比较长。但是就像self一样所有的参数都有它们的传统名称。因此在真实的产品代码中一个元类应该是像这样的
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方法的话我们还可以使它变得更清晰一些。
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的实例
我们通过创建一个类似Django中的ORM来熟悉一下元类的使用通常元类用来创建API是非常好的选择使用元类的编写很复杂但使用者可以非常简洁的调用API。
- 我们想创建一个类似Django的ORM只要定义字段就可以实现对数据库表和字段的操作。
class User(Model):# 定义类的属性到列的映射id IntegerField(id)name StringField(username)email StringField(email)password StringField(password)例如
# 创建一个实例
u User(id12345, nameMichael, emailtestorm.org, passwordmy-pwd)
# 保存到数据库
u.save()接下来我么来实现这么个功能
一、首先来定义Field类它负责保存数据库表的字段名和字段类型
#coding:utf-8
class Field(object):def __init__(self, name, column_type):self.name nameself.column_type column_typedef __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):定义元类def __new__(cls, name, bases, attrs):if nameModel:return super(ModelMetaclass,cls).__new__(cls, name, bases, attrs)mappings dict()for k, v in attrs.iteritems():# 保存类属性和列的映射关系到mappings字典if isinstance(v, Field):print(Found mapping: %s%s % (k, v))mappings[k] vfor k in mappings.iterkeys():#将类属性移除使定义的类字段不污染User类属性只在实例中可以访问这些keyattrs.pop(k)attrs[__table__] name.lower() # 假设表名和为类名的小写,创建类时添加一个__table__类属性attrs[__mappings__] mappings # 保存属性和列的映射关系创建类时添加一个__mappings__类属性return super(ModelMetaclass,cls).__new__(cls, name, bases, attrs)三、编写Model基类
class Model(dict):__metaclass__ ModelMetaclassdef __init__(self, **kw):super(Model, self).__init__(**kw)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(rModel object has no attribute %s % key)def __setattr__(self, key, value):self[key] valuedef 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(id12345, nameMichael, emailtestorm.org, passwordmy-pwd)
# 保存到数据库
u.save()输出
Found mapping: emailStringField:email
Found mapping: passwordStringField:password
Found mapping: idIntegerField:id
Found mapping: nameStringField:username
SQL: insert into User (password,email,username,id) values (?,?,?,?)
ARGS: [my-pwd, testorm.org, Michael, 12345]五、使用__new__方法和元类方式分别实现单例模式
1、new、init、__call__的介绍
在讲到使用元类创建单例模式之前比需了解__new__这个内置方法的作用在上面讲元类的时候我们用到了__new__方法来实现类的创建。然而我在那之前还是对__new__这个方法和__init__方法有一定的疑惑。因此这里花点时间对其概念做一次了解和区分。
__new__方法负责创建一个实例对象在对象被创建的时候调用该方法它是一个类方法。__new__方法在返回一个实例之后会自动的调用__init__方法对实例进行初始化。如果__new__方法不返回值或者返回的不是实例那么它就不会自动的去调用__init__方法。
init 方法负责将该实例对象进行初始化在对象被创建之后调用该方法在__new__方法创建出一个实例后对实例属性进行初始化。__init__方法可以没有返回值。
__call__方法其实和类的创建过程和实例化没有多大关系了定义了__call__方法才能被使用函数的方式执行。
例如
class A(object):def __call__(self):print __call__ be calleda A()
a()
输出:
__call__ be called 打个比方帮助理解如果将创建实例的过程比作建一个房子。
那么class就是一个房屋的设计图他规定了这个房子有几个房间每个人房间的大小朝向等。这个设计图就是累的结构 __new__就是一个房屋的框架每个具体的房屋都需要先搭好框架后才能进行专修当然现有了房屋设计才能有具体的房屋框架出来。这个就是从类到类实例的创建。 __init__就是装修房子的过程对房屋的墙面和地板等颜色材质的丰富就是它该做的事情当然先有具体的房子框架出来才能进行装饰了。这个就是实例属性的初始化它是在__new__出一个实例后才能初始化。 __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 __initself.value valuef1 Foo(1)
print f1.value
f2 Foo(2)
print f2.value#输出
excute __new__
excute __init
1
excute __new__
excute __init
2- 可以看出new方法在init方法之前执行子类如果重写__new__方法一般依然要调用父类的__new__方法。
class Child(Foo):def __new__(cls, *args, **kwargs): return suyper(Child, cls).__new__(cls, *args, **kwargs)必须注意的是类的__new__方法之后必须生成本类的实例才能自动调用本类的__init__方法进行初始化否则不会自动调用__init__.
class Foo(object):def __init__(self, *args, **kwargs):print Foo __init__def __new__(cls, *args, **kwargs):return object.__new__(Stranger, *args, **kwargs)class Stranger(object):def __init__(self,name):print class Strangers __init__ be calledself.name namefoo Foo(test)
print type(foo)
print foo.name 输出
class __main__.Stranger
AttributeError: Stranger object has no attribute name说明如果new方法返回的不是本类的实例那么本类Foo的init和生成的类(Stranger)的init都不会被调用
2、实现单例模式
依照Python官方文档的说法__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple) 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。接下来我们分别通过这两种方式来实现单例模式。当初在看到cookbook中的元类来实现单例模式的时候对其相当疑惑因此才有了上面这些对元类的总结。
简单来说单例模式的原理就是通过在类属性中添加一个单例判定位ins_flag通过这个flag判断是否已经被实例化过了,如果被实例化过了就返回该实例。
1)__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._instances1 Singleton()
s2 Singleton()print s1 is s2因为重写__new__方法所以继承至Singleton的类在不重写__new__的情况下都将是单例模式。
2元类实现单例
当初我也很疑惑为什么我们是从写使用元类的__init__方法而不是使用__new__方法来初为元类增加一个属性。其实我只是上面那一段关于元类中__new__方法迷惑了它主要用于我们需要对类的结构进行改变的时候我们才要重写这个方法。
class Singleton(type):def __init__(self, *args, **kwargs):print __init__self.__instance Nonesuper(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.__instanceclass 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输出
__init__
__call__
__call__
{__module__: __main__, __metaclass__: class __main__.Singleton, _Singleton__instance: __main__.Foo object at 0x100c52f10, __dict__: attribute __dict__ of Foo objects, __weakref__: attribute __weakref__ of Foo objects, __doc__: None}
True基于这个例子
我们知道元类(Singleton)生成的实例是一个类(Foo),而这里我们仅仅需要对这个实例(Foo)增加一个属性(__instance)来判断和保存生成的单例。想想也知道为一个类添加一个属性当然是在__init__中实现了。关于__call__方法的调用因为Foo是Singleton的一个实例。所以Foo()这样的方式就调用了Singleton的__call__方法。不明白就回头看看上一节中的__call__方法介绍。
假如我们通过元类的__new__方法来也可以实现但显然没有通过__init__来实现优雅因为我们不会为了为实例增加一个属性而重写__new__方法。所以这个形式不推荐。
class Singleton(type):def __new__(cls, name,bases,attrs):print __new__attrs[_instance] Nonereturn super(Singleton,cls).__new__(cls,name,bases,attrs)def __call__(self, *args, **kwargs):print __call__if self._instance is None:self._instance super(Singleton,self).__call__(*args, **kwargs)return self._instanceclass Foo(object):__metaclass__ Singletonfoo1 Foo()
foo2 Foo()
print Foo.__dict__
print foo1 is foo2 # True输出
__new__
__call__
__call__
{__module__: __main__, __metaclass__: class __main__.Singleton, _instance: __main__.Foo object at 0x103e07ed0, __dict__: attribute __dict__ of Foo objects, __weakref__: attribute __weakref__ of Foo objects, __doc__: None}
True连接
《深刻理解Python中的元类(metaclass)以及元类实现单例模式》