Python Object Oriented Programming - metaclass

Python Object Oriented Programming - metaclass

First, just reserve

exec:三个参数

参数一:字符串形式的命令

参数二:全局作用域(字典形式),如果不指定,默认为globals()

参数三:局部作用域(字典形式),如果不指定,默认为locals()

The use exec

#可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
g={
'x':1,
'y':2
}
l={}

exec('''
global x,z
x=100
z=200

m=300
''',g,l)

print(g) #{'x': 100, 'y': 2,'z':200,......}
print(l) #{'m': 300}

Second, primers (class is also an object)

class Foo:
      pass

f1=Foo() #f1是通过Foo类实例化的对象

python, everything is an object, the class itself is a target, when using the keyword class, python interpreter will create an object in the loaded class time (here refers to an object is an instance of class instead of the class), thus we can class as an object to use the same concept to meet the first-class objects, you can:

The class assignment to a variable

Class as a function of the parameter passed

The class as the return value of the function

Create classes dynamically at runtime

Examples can be seen on the object f1 is generated by the Foo class, and Foo also an object itself, and that it in turn which class to generate it?

#type函数可以查看类型,也可以用来查看对象的类,二者是一样的
print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
print(type(Foo)) # 输出:<type 'type'>

Third, what is the metaclass

Yuan class is a class of class is a template class.

Yuan class is used to control how to create a template class, as the class is to create objects of the same, but the main purpose is to control the metaclass class is created behavior

Examples of the results of our metaclass class using the class definition, as the object class instance (f1 object is an instance of class Foo, Foo class is a class instance of type)

a built-in type is the metaclass python, used to directly control the generated class, any class python class definition are in fact an object instance of the class type.

Fourth, the two ways to create a class

Method 1: Use the class keyword

class Chinese(object):
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def talk(self):
        print('%s is talking' %self.name)

Second way: class is created manually analog class procedure): A step of creating classes split open, manually to create

#准备工作:

#创建类主要分为三部分

  1 类名

  2 类的父类

  3 类体


#类名
class_name='Chinese'
#类的父类
class_bases=(object,)
#类体
class_body="""
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age
def talk(self):
    print('%s is talking' %self.name)
"""

Step one (first processing class body -> namespace): Class body defined name will be deposited in the name of the space class (a local name space), we can pre-define an empty dictionary, and then exec to perform like body Code (exec produced during the namespace class real process is similar, but the latter deformation properties will begin with __), the local name space generated classes, i.e. filled dictionary.

class_dic={}
exec(class_body,globals(),class_dic)


print(class_dic)
#{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}

Step two: metaclass call type (can also be customized) to produce class Chinense.

Foo=type(class_name,class_bases,class_dic) #实例化type得到对象Foo,即我们用class定义的类Foo


print(Foo)
print(type(Foo))
print(isinstance(Foo,type))
'''
<class '__main__.Chinese'>
<class 'type'>
True
'''

We see, type accepts three parameters:

  • The first argument is the string 'Foo', the class name represents
  • The second parameter is the tuple (object,), represents all of the parent
  • The third parameter is a dictionary, this is an empty dictionary, does not define the properties and methods represent

NOTE: If Foo class inheritance, i.e. class Foo (Bar): .... is equivalent to the type ( 'Foo', (Bar,), {})

Fifth, custom metaclass control the behavior of the class

#一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的行为,工作流程是什么)

5 steps take you to learn metaclass (from egon teacher)

#知识储备:
    #产生的新对象 = object.__new__(继承object类的子类)

#步骤一:如果说People=type(类名,类的父类们,类的名称空间),那么我们定义元类如下,来控制类的创建
class Mymeta(type):  # 继承默认元类的一堆属性
    def __init__(self, class_name, class_bases, class_dic):
        if '__doc__' not in class_dic or not class_dic.get('__doc__').strip():
            raise TypeError('必须为类指定文档注释')

        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')

        super(Mymeta, self).__init__(class_name, class_bases, class_dic)


class People(object, metaclass=Mymeta):
    country = 'China'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def talk(self):
        print('%s is talking' % self.name)

#步骤二:如果我们想控制类实例化的行为,那么需要先储备知识__call__方法的使用
class People(object,metaclass=type):
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __call__(self, *args, **kwargs):
        print(self,args,kwargs)


# 调用类People,并不会出发__call__
obj=People('egon',18)

# 调用对象obj(1,2,3,a=1,b=2,c=3),才会出发对象的绑定方法obj.__call__(1,2,3,a=1,b=2,c=3)
obj(1,2,3,a=1,b=2,c=3) #打印:<__main__.People object at 0x10076dd30> (1, 2, 3) {'a': 1, 'b': 2, 'c': 3}

#总结:如果说类People是元类type的实例,那么在元类type内肯定也有一个__call__,会在调用People('egon',18)时触发执行,然后返回一个初始化好了的对象obj

#步骤三:自定义元类,控制类的调用(即实例化)的过程
class Mymeta(type): #继承默认元类的一堆属性
    def __init__(self,class_name,class_bases,class_dic):
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')

        super(Mymeta,self).__init__(class_name,class_bases,class_dic)

    def __call__(self, *args, **kwargs):
        #self=People
        print(self,args,kwargs) #<class '__main__.People'> ('egon', 18) {}

        #1、实例化People,产生空对象obj
        obj=object.__new__(self)


        #2、调用People下的函数__init__,初始化obj
        self.__init__(obj,*args,**kwargs)


        #3、返回初始化好了的obj
        return obj

class People(object,metaclass=Mymeta):
    country='China'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def talk(self):
        print('%s is talking' %self.name)

obj=People('egon',18)
print(obj.__dict__) #{'name': 'egon', 'age': 18}

#步骤四:
class Mymeta(type): #继承默认元类的一堆属性
    def __init__(self,class_name,class_bases,class_dic):
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')

        super(Mymeta,self).__init__(class_name,class_bases,class_dic)

    def __call__(self, *args, **kwargs):
        #self=People
        print(self,args,kwargs) #<class '__main__.People'> ('egon', 18) {}

        #1、调用self,即People下的函数__new__,在该函数内完成:1、产生空对象obj 2、初始化 3、返回obj
        obj=self.__new__(self,*args,**kwargs)

        #2、一定记得返回obj,因为实例化People(...)取得就是__call__的返回值
        return obj

class People(object,metaclass=Mymeta):
    country='China'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def talk(self):
        print('%s is talking' %self.name)

    def __new__(cls, *args, **kwargs):
        obj=object.__new__(cls)
        cls.__init__(obj,*args,**kwargs)
        return obj

obj=People('egon',18)
print(obj.__dict__) #{'name': 'egon', 'age': 18}

#步骤五:基于元类实现单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存
class Mysql:
    __instance=None
    def __init__(self,host='127.0.0.1',port='3306'):
        self.host=host
        self.port=port

    @classmethod
    def singleton(cls,*args,**kwargs):
        if not cls.__instance:
            cls.__instance=cls(*args,**kwargs)
        return cls.__instance


obj1=Mysql()
obj2=Mysql()
print(obj1 is obj2) #False

obj3=Mysql.singleton()
obj4=Mysql.singleton()
print(obj3 is obj4) #True

#应用:定制元类实现单例模式
class Mymeta(type):
    def __init__(self,name,bases,dic): #定义类Mysql时就触发
        self.__instance=None
        super().__init__(name,bases,dic)

    def __call__(self, *args, **kwargs): #Mysql(...)时触发

        if not self.__instance:
            self.__instance=object.__new__(self) #产生对象
            self.__init__(self.__instance,*args,**kwargs) #初始化对象
            #上述两步可以合成下面一步
            # self.__instance=super().__call__(*args,**kwargs)

        return self.__instance
class Mysql(metaclass=Mymeta):
    def __init__(self,host='127.0.0.1',port='3306'):
        self.host=host
        self.port=port


obj1=Mysql()
obj2=Mysql()

print(obj1 is obj2)

Guess you like

Origin www.cnblogs.com/Kwan-C/p/11536479.html