元类介绍

Python中的类

记住一句话:在Python中万事万物皆对象
python中的类也是对象,当一个对象具有创建对象的能力时,就称该对象为类。既然类也是对象,那他就有一下属性

  • 可以将它赋值给其他变量
  • 可以给他添加属性
  • 它可以作为函数的参数传递以及作为返回值返回
class Person(object):
    def __init__(self,name):
        self.name = name

    def say(self):
        print('hello,{}'.format(self.name))


# 赋值给其他变量
xm = Person
xiaomigng = xm('xiaoming')
print(xiaomigng.name)
xiaomigng.say()

# 添加属性
print(hasattr(Person, 'country'))
Person.country = 'china'
print(hasattr(Person, 'country'))


# 作为函数的参数
def exmp(person):
    print(person)
          
  
exmp(Person)

类的几个常用方法

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(self)
        print('this is __init___')

    def __call__(self, *args, **kwargs):
        print('this is __call___')

    def __new__(cls, name, age):
        print(cls)
        print('this is __new___')
        return super(Person, cls).__new__(cls)


person = Person('ctz', 22)
person()
'''
<class '__main__.Person'>
this is __new___
<__main__.Person object at 0x00000286F4C70E80>
this is __init___
this is __call___
'''
  • __ new __方法
    __ new __ 方法接受的参数和 __ init __ 一样,但 __ init __ 是在类实例创建之后调用,而 __ new __ 方法正是创建这个类实例的方法。 __ init __ 方法里面的self 实际就是 __ new __ 所创建的实例, __ new __ 返回值就是创建的实例,属于类级别的方法

  • __ init __ 方法
    .__ init __ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。无返回值

  • __ call __ 方法
    构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于
    __ call __ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

使用type动态的创建类

type是所有新式类的类,所有类都可以说是type创建的
object是所有新式类的父类

print(object.__class__)
print(object.__bases__)
print(object.__class__)
print(type.__bases__)
'''
<class 'type'>   # object类是一个type元类所创建
()               # 说明 object类已经处于继承链条的顶端,是所有类的父类。
<class 'type'>   #   type自身的类就是type,就是说type元类也就是由type自身创建的。
(<class 'object'>,)   # type这一元类的父类是object。
'''

type动态创建类

type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

def say(self):
  print('hello,{}' .format(self.name))


def sayChinese(self):
  print('你好,{}'.format(self.name))


address = input('input your country:').strip()
name = input('your name:').strip()
dic = {}
dic['name'] = name
if address == 'china':
  dic['country'] = '中国'
  dic['say'] = sayChinese
else:
  dic['county'] = 'other'
  dic['say'] = say

Person = type('Person', (object,), dic)
print(Person)

person = Person()
print(person.name)
person.say()

也可以继承其他类

class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def walk(self):
        print('{} is walking'.format(self.name))


def eat(self):
    print('{} is eatting'.format(self.name))


Dog = type('Dog', (Animal,), {'eat': eat})

dog = Dog('aa', 2)
print(dog.name)
dog.eat()
dog.walk()

Python中的元类

元类就是用来创建这些类(对象)的,元类就是类的类,我们上面用了type来创建类,因为type就是一个元类,我们可以看下面代码

a = 5
b = 'bb'
c = True

def fun():
    pass


class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age


print(a.__class__.__class__)
print(b.__class__.__class__)
print(fun.__class__.__class__)
print(Person.__class__)
'''
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
'''

type就是Python在背后用来创建所有类的元类

__ metaclass __ 属性

class Foo(object):
    __metaclass__ = something…

Python就会用元类来创建类Foo。首先我们写下class Foo(object),但是类对象Foo还没有在内存中创建。Python会在类的定义中寻找metaclass属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类

class Foo(Bar):
    pass

Python做了如下的操作:

Foo中有 __ metaclass __ 这个属性吗?如果是,Python会在内存中通过__ metaclass __创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到 __ metaclass __,它会继续在Bar(父类)中寻找 __ metaclass __属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到 __metaclass __,它就会在模块层次中去寻找 __metaclass __,并尝试做同样的操作。如果还是找不到 __ metaclass __,Python就会用内置的type来创建这个类对象。

现在的问题就是,你可以在 __ metaclass __中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。

# 1.对象是类创建,创建对象时候类的__new__和__init__方法自动执行,对象()执行类的 __call__ 方法
# 2.类是type创建,创建类时候type的__new__,__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)


# 第0步: 执行type的 __new__和__init__ 方法【类是type的对象】
class Foo(object):
    def __init__(self):
        print('类的__init__')
        pass

    def __call__(self, *args, **kwargs):
        print('类的__call__')
        pass


# 第1步: 执行type的 __call__ 方法
#        1.1  调用 Foo类(是type的对象)的 __new__方法,用于创建对象。
#        1.2  调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
obj = Foo()
# 第2步:执行Foo类 __call__ 方法
obj()

示例一

class MyType(type):
    def __init__(self, *args, **kwargs):
        print('MyType创建类__init__',self)
        super(MyType, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        obj = super(MyType, self).__call__(*args, **kwargs)
        print('类创建对象__init__', self, obj)
        return obj

    def __new__(cls, *args, **kwargs):
        print('Mytype __new__', cls)
        return super(MyType, cls).__new__(cls, *args, **kwargs)


class Foo(object,metaclass=MyType):
    user = 'ctz'
    age = 18


obj = Foo()

'''
Mytype __new__ <class '__main__.MyType'>
MyType创建类 <class '__main__.Foo'>
类创建对象 <class '__main__.Foo'> <__main__.Foo object at 0x00000227D23BE630>
'''

示例二

class MyType(type):

    def __init__(self, *args, **kwargs):
        print('MyType __init__', self, '----')
        super(MyType, self).__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        v = dir(cls)
        obj = super(MyType, cls).__call__(*args, **kwargs)
        print('MyType __call__', cls, obj, '****')
        return obj

    def __new__(cls, *args, **kwargs):
        print('Mytype __new__',cls)
        return super(MyType,cls).__new__(cls, *args, **kwargs)


class Foo(MyType('Bar', (object,), {})):

    user = 'ctz'
    age = 18

    def __init__(self, *args, **kwargs):
        super(Foo, self).__init__(*args, **kwargs)


obj = Foo()
print(Foo.__bases__)
print(Foo.__class__)


'''
D:\Python36\python.exe D:/test/python高效编程技巧/元类介绍/元类示例2.py
Mytype __new__ <class '__main__.MyType'>
MyType __init__ <class '__main__.Bar'> ----
Mytype __new__ <class '__main__.MyType'>
MyType __init__ <class '__main__.Foo'> ----
MyType __call__ <class '__main__.Foo'> <__main__.Foo object at 0x0000016BE87DEAC8> ****
(<class '__main__.Bar'>,)
<class '__main__.MyType'>
'''

上述相当于我们用MyType创建了一个类Bar,然后用Foo继承Bar,当创建Foo类的时候我们在Foo中找不到__ metaclass __ 属性,再去Bar里面找,Bar里面的 __ metaclass __指向MyType,所以MyType也是Foo的元类,用它来创建Foo

示例三

class MyType(type):
    def __init__(self, *args, **kwargs):
        print(self,'------')
        super(MyType, self).__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        v = dir(cls)
        obj = super(MyType, cls).__call__(*args, **kwargs)
        print(cls,obj,'****')
        return obj


def with_metaclass(arg,base):
    return MyType('MyType', (base,), {})


class Foo(with_metaclass(MyType,object)):
    user = 'ctz'
    age = 18


obj = Foo()

'''
<class '__main__.MyType'> ------
<class '__main__.Foo'> ------
<class '__main__.Foo'> <__main__.Foo object at 0x000001FBD9E63710> ****
'''

这种和上面第二种没啥区别,知识封装了一下,不过查看Flask,wtforms源码可知,实例化Form就是通过这种方法做的

wtforms源码参考


def with_metaclass(meta, base=object):
    return meta("NewBase", (base,), {})


class Form(with_metaclass(FormMeta, BaseForm)):
    """
    Declarative Form base class. Extends BaseForm's core behaviour allowing
    fields to be defined on Form subclasses as class attributes.

    In addition, form and instance input data are taken at construction time
    and passed to `process()`.
    """
    Meta = DefaultMeta

    def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
        """
        :param formdata:
            Used to pass data coming from the enduser, usually `request.POST` or
            equivalent. formdata should be some sort of request-data wrapper which
            can get multiple parameters from the form input, and values are unicode
            strings, e.g. a Werkzeug/Django/WebOb MultiDict
        :param obj:
            If `formdata` is empty or not provided, this object is checked for
            attributes matching form field names, which will be used for field
            values.
        :param prefix:
            If provided, all fields will have their name prefixed with the
            value.
        :param data:
            Accept a dictionary of data. This is only used if `formdata` and
            `obj` are not present.
        :param meta:
            If provided, this is a dictionary of values to override attributes
            on this form's meta instance.
        :param `**kwargs`:
            If `formdata` is empty or not provided and `obj` does not contain
            an attribute named the same as a field, form will assign the value
            of a matching keyword argument to the field, if one exists.
        """
        meta_obj = self._wtforms_meta()
        if meta is not None and isinstance(meta, dict):
            meta_obj.update_values(meta)
        super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)

元类实现单例模式

import threading


class Singleton(type):
    _instance_lock = threading.Lock()  # 为了实现支持多线程的单例模式

    def __call__(cls, *args, **kwargs):
        with cls._instance_lock:
            if not hasattr(cls,'_instance'):
                cls._instance=super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instance


class Foo(metaclass=Singleton):
    def __init__(self, name):
        self.name = name


obj1 = Foo('ctz')
obj2 = Foo('ztc')
print(obj1, obj2)

'''
<__main__.Foo object at 0x000002155BBDE940> <__main__.Foo object at 0x000002155BBDE940>
'''

自定义元类

示例1

class UpperTest(type):
    def __new__(cls, myclass, parentclass, myattrs):
        attrs = ((name, value) for name, value in myattrs.items() if not name.startswith('__'))
        attrs_dic = dict((k.upper(), v) for k, v in attrs)
        return type(myclass, parentclass, attrs_dic)


class MyTest(object, metaclass=UpperTest):
    name = 'ctz'
    age = 22


print(hasattr(MyTest, 'name'))
print(hasattr(MyTest, 'age'))
print(hasattr(MyTest, 'NAME'))
print(hasattr(MyTest, 'AGE'))


'''
D:\Python36\python.exe D:/test/python高效编程技巧/元类介绍/自定义元类1.py
False
False
True
True
'''

示例2

class MetaList(type):
    def __new__(cls, myclassname, myparentname,attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, myclassname, myparentname, attrs)


class MyList(list, metaclass=MetaList):
    pass


l = MyList()
l.add(1)
l.add(2)
print(l)

应用Django-----ORM

创建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)
  

创建StringField和IntergerField

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')

继承Field,示例化是调用Field的初始化方法、

创建Model元类

class ModelMetaclass(type):
 
    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        attrs['__table__'] = name # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)

它做了以下几件事

  • 创建一个新的字典mapping
  • 将每一个类的属性,通过.items()遍历其键值对。如果值是Field类,则打印键值,并将这一对键值绑定到mapping字典上。
  • 将刚刚传入值为Field类的属性删除。
  • 创建一个专门的mappings属性,保存字典mapping。
  • 创建一个专门的table属性,保存传入的类的名称。

创建Model类

class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kwarg):
        super(Model, self).__init__(**kwarg)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError("'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    # 模拟建表操作
    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

创建Model的子类User

class User(Model):
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

思路:

当执行 id = IntegerField('id')时首先会调用元类的 __ new __ 方法,由于IntegerField是Field的子类,所以会将其存入 __ mapping __中,
mapping['id']=IntegerField('id')并从attrs中删除这个键值对,然后调用
Model. __ setattr __(self, ‘id’, IntegerField(‘id’))

初始化实例

u = User(id=1, name='ctz', email='ctz@qq.com', password='123')
u.save()
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')


class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)



class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kwarg):
        super(Model, self).__init__(**kwarg)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError("'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    # 模拟建表操作
    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)  # v就表示Filed或者其子类
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))



class User(Model):
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')


u = User(id=1, name='ctz', email='ctz@qq.com', password='123')
u.save()

'''
Found model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
SQL: insert into User (id,username,email,password) values (1,ctz,ctz@qq.com,123)
ARGS: [1, 'ctz', 'ctz@qq.com', '123']
'''

"""
1.创建User实例时,调用元类的__new__,如果是Filed字段,将其价值对存放在mapping中类似于:
   mapping['id] = IntegerField('id'),然后将其键值对从attrs中删除,然后将mapping赋值给attrs['__mapping__']
2. 调用Model.__setattr__(self, ‘id’, IntegerField(‘id’))进行赋值
"""
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,654评论 18 139
  • 了解元类之前,先了解几个魔术方法: __new__、__init__、__call__ __new__: 对象的创...
    大富帅阅读 9,127评论 2 16
  • 1.元类 1.1.1类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在Python中这...
    TENG书阅读 1,268评论 0 3
  • 类也是对象,在理解元类之前,你需要先掌握Python中的类。Python中类的概念借鉴于Smalltalk,这显得...
    雲凌禹阅读 452评论 0 3
  • 我遇到过一只狗和一只猫。 狗是收养来的。据说最开始的主人脾气不好,小狗从一下生就受到虐待,包括但不限于拳头暴力。后...
    康加罗阅读 163评论 0 0