class type(object)| type(object) -> the object's type| type(name, bases, dict) -> a new type
元类需要3个参数。'姓名'、'基地'和'字典'
这里是秘密开始的地方。在这个示例类定义中查找名称、基数和字典的来源。
class ThisIsTheName(Bases, Are, Here):All_the_code_heredef doesIs(create, a):dict
让我们定义一个元类来演示'班级:'如何调用它。
def test_metaclass(name, bases, dict):print 'The Class Name is', nameprint 'The Class Bases are', basesprint 'The dict has', len(dict), 'elems, the keys are', dict.keys()
return "yellow"
class TestName(object, None, int, 1):__metaclass__ = test_metaclassfoo = 1def baz(self, arr):pass
print 'TestName = ', repr(TestName)
# output =>The Class Name is TestNameThe Class Bases are (<type 'object'>, None, <type 'int'>, 1)The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']TestName = 'yellow'
class MetaSingleton(type):instance = Nonedef __call__(cls, *args, **kw):if cls.instance is None:cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)return cls.instance
class Foo(object):__metaclass__ = MetaSingleton
a = Foo()b = Foo()assert a is b
def make_hook(f):"""Decorator to turn 'foo' method into '__foo__'"""f.is_hook = 1return f
class MyType(type):def __new__(mcls, name, bases, attrs):
if name.startswith('None'):return None
# Go over attributes and see if they should be renamed.newattrs = {}for attrname, attrvalue in attrs.iteritems():if getattr(attrvalue, 'is_hook', 0):newattrs['__%s__' % attrname] = attrvalueelse:newattrs[attrname] = attrvalue
return super(MyType, mcls).__new__(mcls, name, bases, newattrs)
def __init__(self, name, bases, attrs):super(MyType, self).__init__(name, bases, attrs)
# classregistry.register(self, self.interfaces)print "Would register class %s now." % self
def __add__(self, other):class AutoClass(self, other):passreturn AutoClass# Alternatively, to autogenerate the classname as well as the class:# return type(self.__name__ + other.__name__, (self, other), {})
def unregister(self):# classregistry.unregister(self)print "Would unregister class %s now." % self
class MyObject:__metaclass__ = MyType
class NoneSample(MyObject):pass
# Will print "NoneType None"print type(NoneSample), repr(NoneSample)
class Example(MyObject):def __init__(self, value):self.value = value@make_hookdef add(self, other):return self.__class__(self.value + other.value)
# Will unregister the classExample.unregister()
inst = Example(10)# Will fail with an AttributeError#inst.unregister()
print inst + instclass Sibling(MyObject):pass
ExampleSibling = Example + Sibling# ExampleSibling is now a subclass of both Example and Sibling (with no# content of its own) although it will believe it's called 'AutoClass'print ExampleSiblingprint ExampleSibling.__mro__
>>> class ObjectCreator(object):... pass...
>>> my_object = ObjectCreator()>>> print(my_object)<__main__.ObjectCreator object at 0x8974f2c>
但是类不仅仅是Python中的类。类也是对象。
是的,物体。
一旦您使用关键字class,Python就会执行它并创建对象.指令
>>> class ObjectCreator(object):... pass...
在内存中创建一个名为ObjectCreator的对象。
这个对象(类)本身能够创建对象(实例),这就是为什么它是一个类。
但是,它仍然是一个对象,因此:
你可以把它赋值给一个变量
你可以复制它
你可以给它添加属性
您可以将其作为函数参数传递
例如:
>>> print(ObjectCreator) # you can print a class because it's an object<class '__main__.ObjectCreator'>>>> def echo(o):... print(o)...>>> echo(ObjectCreator) # you can pass a class as a parameter<class '__main__.ObjectCreator'>>>> print(hasattr(ObjectCreator, 'new_attribute'))False>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class>>> print(hasattr(ObjectCreator, 'new_attribute'))True>>> print(ObjectCreator.new_attribute)foo>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable>>> print(ObjectCreatorMirror.new_attribute)foo>>> print(ObjectCreatorMirror())<__main__.ObjectCreator object at 0x8997b4c>
动态创建类
由于类是对象,您可以像任何对象一样动态创建它们。
首先,您可以使用class在函数中创建一个类:
>>> def choose_class(name):... if name == 'foo':... class Foo(object):... pass... return Foo # return the class, not an instance... else:... class Bar(object):... pass... return Bar...>>> MyClass = choose_class('foo')>>> print(MyClass) # the function returns a class, not an instance<class '__main__.Foo'>>>> print(MyClass()) # you can create an object from this class<__main__.Foo object at 0x89c6d4c>
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object>>> print(MyShinyClass)<class '__main__.MyShinyClass'>>>> print(MyShinyClass()) # create an instance with the class<__main__.MyShinyClass object at 0x8997cec>
# the metaclass will automatically get passed the same argument# that you usually pass to `type`def upper_attr(future_class_name, future_class_parents, future_class_attrs):"""Return a class object, with the list of its attribute turnedinto uppercase."""# pick up any attribute that doesn't start with '__' and uppercase ituppercase_attrs = {attr if attr.startswith("__") else attr.upper(): vfor attr, v in future_class_attrs.items()}
# let `type` do the class creationreturn type(future_class_name, future_class_parents, uppercase_attrs)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though# but we can define __metaclass__ here instead to affect only this class# and this will work with "object" childrenbar = 'bip'
# remember that `type` is actually a class like `str` and `int`# so you can inherit from itclass UpperAttrMetaclass(type):# __new__ is the method called before __init__# it's the method that creates the object and returns it# while __init__ just initializes the object passed as parameter# you rarely use __new__, except when you want to control how the object# is created.# here the created object is the class, and we want to customize it# so we override __new__# you can do some stuff in __init__ too if you wish# some advanced use involves overriding __call__ as well, but we won't# see thisdef __new__(upperattr_metaclass, future_class_name,future_class_parents, future_class_attrs):uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): vfor attr, v in future_class_attrs.items()}return type(future_class_name, future_class_parents, uppercase_attrs)
让我们重写上面的内容,但是使用更短、更现实的变量名,现在我们知道它们的意思了:
class UpperAttrMetaclass(type):def __new__(cls, clsname, bases, attrs):uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): vfor attr, v in attrs.items()}return type(clsname, bases, uppercase_attrs)
#!/usr/bin/env python
# Copyright (C) 2013-2014 Craig Phillips. All rights reserved.
# This requires some explaining. The point of this metaclass excercise is to# create a static abstract class that is in one way or another, dormant until# queried. I experimented with creating a singlton on import, but that did# not quite behave how I wanted it to. See now here, we are creating a class# called GsyncOptions, that on import, will do nothing except state that its# class creator is GsyncOptionsType. This means, docopt doesn't parse any# of the help document, nor does it start processing command line options.# So importing this module becomes really efficient. The complicated bit# comes from requiring the GsyncOptions class to be static. By that, I mean# any property on it, may or may not exist, since they are not statically# defined; so I can't simply just define the class with a whole bunch of# properties that are @property @staticmethods.## So here's how it works:## Executing 'from libgsync.options import GsyncOptions' does nothing more# than load up this module, define the Type and the Class and import them# into the callers namespace. Simple.## Invoking 'GsyncOptions.debug' for the first time, or any other property# causes the __metaclass__ __getattr__ method to be called, since the class# is not instantiated as a class instance yet. The __getattr__ method on# the type then initialises the class (GsyncOptions) via the __initialiseClass# method. This is the first and only time the class will actually have its# dictionary statically populated. The docopt module is invoked to parse the# usage document and generate command line options from it. These are then# paired with their defaults and what's in sys.argv. After all that, we# setup some dynamic properties that could not be defined by their name in# the usage, before everything is then transplanted onto the actual class# object (or static class GsyncOptions).## Another piece of magic, is to allow command line options to be set in# in their native form and be translated into argparse style properties.## Finally, the GsyncListOptions class is actually where the options are# stored. This only acts as a mechanism for storing options as lists, to# allow aggregation of duplicate options or options that can be specified# multiple times. The __getattr__ call hides this by default, returning the# last item in a property's list. However, if the entire list is required,# calling the 'list()' method on the GsyncOptions class, returns a reference# to the GsyncListOptions class, which contains all of the same properties# but as lists and without the duplication of having them as both lists and# static singlton values.## So this actually means that GsyncOptions is actually a static proxy class...## ...And all this is neatly hidden within a closure for safe keeping.def GetGsyncOptionsType():class GsyncListOptions(object):__initialised = False
class GsyncOptionsType(type):def __initialiseClass(cls):if GsyncListOptions._GsyncListOptions__initialised: return
from docopt import docoptfrom libgsync.options import docfrom libgsync import __version__
options = docopt(doc.__doc__ % __version__,version = __version__,options_first = True)
paths = options.pop('<path>', None)setattr(cls, "destination_path", paths.pop() if paths else None)setattr(cls, "source_paths", paths)setattr(cls, "options", options)
for k, v in options.iteritems():setattr(cls, k, v)
GsyncListOptions._GsyncListOptions__initialised = True
def list(cls):return GsyncListOptions
def __getattr__(cls, name):cls.__initialiseClass()return getattr(GsyncListOptions, name)[-1]
def __setattr__(cls, name, value):# Substitut option names: --an-option-name for an_option_nameimport rename = re.sub(r'^__', "", re.sub(r'-', "_", name))listvalue = []
# Ensure value is converted to a list type for GsyncListOptionsif isinstance(value, list):if value:listvalue = [] + valueelse:listvalue = [ None ]else:listvalue = [ value ]
type.__setattr__(GsyncListOptions, name, listvalue)
# Cleanup this module to prevent tinkering.import sysmodule = sys.modules[__name__]del module.__dict__['GetGsyncOptionsType']
return GsyncOptionsType
# Our singlton abstract proxy class.class GsyncOptions(object):__metaclass__ = GetGsyncOptionsType()
When you write a class definition and Python executes it, it uses a metaclass to instantiate the class object (which will, in turn, be used to instantiate instances of that class).
Just as we can use class definitions to change how custom object instances behave, we can use a metaclass class definition to change the way a class object behaves.
The potential uses for metaclasses are boundless. Some ideas that have been explored include logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.
Nevertheless, it is usually encouraged for users to avoid using metaclasses unless absolutely necessary.
You use a metaclass every time you create a class:
When you write a class definition, for example, like this,
class Person(metaclass=Validator):weight = ValidateType(int)age = ValidateType(int)name = ValidateType(str)
这是元类的样子(不使用__prepare__,因为不需要它):
class Validator(type):def __new__(metacls, cls, bases, clsdict):# search clsdict looking for ValidateType descriptorsfor name, attr in clsdict.items():if isinstance(attr, ValidateType):attr.name = nameattr.attr = '_' + name# create final class and return itreturn super().__new__(metacls, cls, bases, clsdict)
示例运行:
p = Person()p.weight = 9print(p.weight)p.weight = '9'
生产:
9Traceback (most recent call last):File "simple_meta.py", line 36, in <module>p.weight = '9'File "simple_meta.py", line 24, in __set__(self.name, self.type, value))TypeError: weight must be of type(s) <class 'int'> (got '9')
说明:这个例子很简单,它也可以用类装饰器来完成,但可能实际的元类会做更多的事情。
用于引用的'ValidateType'类:
class ValidateType:def __init__(self, type):self.name = None # will be set by metaclassself.attr = None # will be set by metaclassself.type = typedef __get__(self, inst, cls):if inst is None:return selfelse:return inst.__dict__[self.attr]def __set__(self, inst, value):if not isinstance(value, self.type):raise TypeError('%s must be of type(s) %s (got %r)' %(self.name, self.type, value))else:inst.__dict__[self.attr] = value
# define a classclass SomeClass(object):# ...# some definition here ...# ...
# create an instance of itinstance = SomeClass()
# then call the object as if it's a functionresult = instance('foo', 'bar')
当您在类上实现__call__()魔术方法时,后者是可能的。
class SomeClass(object):# ...# some definition here ...# ...
def __call__(self, foo, bar):return bar + foo
class Meta_1(type):def __call__(cls):print "Meta_1.__call__() before creating an instance of ", clsinstance = super(Meta_1, cls).__call__()print "Meta_1.__call__() about to return instance."return instance
这是一个使用元类的类
class Class_1(object):
__metaclass__ = Meta_1
def __new__(cls):print "Class_1.__new__() before creating an instance."instance = super(Class_1, cls).__new__(cls)print "Class_1.__new__() about to return instance."return instance
def __init__(self):print "entering Class_1.__init__() for instance initialization."super(Class_1,self).__init__()print "exiting Class_1.__init__()."
现在让我们创建一个Class_1的实例
instance = Class_1()# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.# Class_1.__new__() before creating an instance.# Class_1.__new__() about to return instance.# entering Class_1.__init__() for instance initialization.# exiting Class_1.__init__().# Meta_1.__call__() about to return instance.
class type:def __call__(cls, *args, **kwarg):
# ... maybe a few things done to cls here
# then we call __new__() on the class to create an instanceinstance = cls.__new__(cls, *args, **kwargs)
# ... maybe a few things done to the instance here
# then we initialize the instance with its __init__() methodinstance.__init__(*args, **kwargs)
# ... maybe a few more things done to instance here
# then we return itreturn instance
class Meta_2(type):singletons = {}
def __call__(cls, *args, **kwargs):if cls in Meta_2.singletons:# we return the only instance and skip a call to __new__()# and __init__()print ("{} singleton returning from Meta_2.__call__(), ""skipping creation of new instance.".format(cls))return Meta_2.singletons[cls]
# else if the singleton isn't present we proceed as usualprint "Meta_2.__call__() before creating an instance."instance = super(Meta_2, cls).__call__(*args, **kwargs)Meta_2.singletons[cls] = instanceprint "Meta_2.__call__() returning new instance."return instance
class Class_2(object):
__metaclass__ = Meta_2
def __new__(cls, *args, **kwargs):print "Class_2.__new__() before creating instance."instance = super(Class_2, cls).__new__(cls)print "Class_2.__new__() returning instance."return instance
def __init__(self, *args, **kwargs):print "entering Class_2.__init__() for initialization."super(Class_2, self).__init__()print "exiting Class_2.__init__()."
让我们观察一下重复尝试创建Class_2类型的对象时会发生什么
a = Class_2()# Meta_2.__call__() before creating an instance.# Class_2.__new__() before creating instance.# Class_2.__new__() returning instance.# entering Class_2.__init__() for initialization.# exiting Class_2.__init__().# Meta_2.__call__() returning new instance.
b = Class_2()# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
c = Class_2()# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
a is b is c # True
class somemeta(type):__new__(mcs, name, bases, clsdict):"""mcs: is the base metaclass, in this case type.name: name of the new class, as provided by the user.bases: tuple of base classesclsdict: a dictionary containing all methods and attributes defined on class
you must return a class object by invoking the __new__ constructor on the base metaclass.ie:return type.__call__(mcs, name, bases, clsdict).
in the following case:
class foo(baseclass):__metaclass__ = somemeta
an_attr = 12
def bar(self):...
@classmethoddef foo(cls):...
arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}
you can modify any of these values before passing on to type"""return type.__call__(mcs, name, bases, clsdict)
def __init__(self, name, bases, clsdict):"""called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton."""pass
def __prepare__():"""returns a dict or something that can be used as a namespace.the type will then attach methods and attributes from class definition to it.
call order :
somemeta.__new__ -> type.__new__ -> type.__init__ -> somemeta.__init__"""return dict()
def mymethod(cls):""" works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls."""pass
class MyClass:__metaclass__ = type# write here other method# write here one more method
print(MyClass.__metaclass__)
它会产生这样的输出:
class 'type'
当然,您可以创建自己的metaclass来定义使用您的类创建的任何类的行为。
为此,必须继承默认的metaclass类型类,因为这是主metaclass:
class MyMetaClass(type):__metaclass__ = type# you can write here any behaviour you want
class MyTestClass:__metaclass__ = MyMetaClass
Obj = MyTestClass()print(Obj.__metaclass__)print(MyMetaClass.__metaclass__)
class MetaMemberControl(type):__slots__ = ()
@classmethoddef __prepare__(mcs, f_cls_name, f_cls_parents, # f_cls means: future classmeta_args=None, meta_options=None): # meta_args and meta_options is not necessarily needed, just so you know.f_cls_attr = dict()if not "do something or if you want to define your cool stuff of dict...":return dict(make_your_special_dict=None)else:return f_cls_attr
def __new__(mcs, f_cls_name, f_cls_parents, f_cls_attr,meta_args=None, meta_options=None):
original_getattr = f_cls_attr.get('__getattribute__')original_setattr = f_cls_attr.get('__setattr__')
def init_getattr(self, item):if not item.startswith('_'): # you can set break points at herealias_name = '_' + itemif alias_name in f_cls_attr['__slots__']:item = alias_nameif original_getattr is not None:return original_getattr(self, item)else:return super(eval(f_cls_name), self).__getattribute__(item)
def init_setattr(self, key, value):if not key.startswith('_') and ('_' + key) in f_cls_attr['__slots__']:raise AttributeError(f"you can't modify private members:_{key}")if original_setattr is not None:original_setattr(self, key, value)else:super(eval(f_cls_name), self).__setattr__(key, value)
f_cls_attr['__getattribute__'] = init_getattrf_cls_attr['__setattr__'] = init_setattr
cls = super().__new__(mcs, f_cls_name, f_cls_parents, f_cls_attr)return cls
class Human(metaclass=MetaMemberControl):__slots__ = ('_age', '_name')
def __init__(self, name, age):self._name = nameself._age = age
def __getattribute__(self, item):"""is just for IDE recognize."""return super().__getattribute__(item)
""" with MetaMemberControl then you don't have to write as following@propertydef name(self):return self._name
@propertydef age(self):return self._age"""
def test_demo():human = Human('Carson', 27)# human.age = 18 # you can't modify private members:_age <-- this is defined by yourself.# human.k = 18 # 'Human' object has no attribute 'k' <-- system error.age1 = human._age # It's OK, although the IDE will show some warnings. (Access to a protected member _age of a class)
age2 = human.age # It's OK! see below:"""if you do not define `__getattribute__` at the class of Human,the IDE will show you: Unresolved attribute reference 'age' for class 'Human'but it's ok on running since the MetaMemberControl will help you."""
if __name__ == '__main__':test_demo()
class PersonMeta(type):def __new__(cls, name, bases, dct):# Modify the attributes of the class before it is createddct['age'] = 0return super().__new__(cls, name, bases, dct)
class Person(metaclass=PersonMeta):def __init__(self, name):self.name = name