python元类:**type()   **

7      参考资料

编号

标题

链接

1

元类

https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

2

描述符

http://python.jobbole.com/81899/

3

《流畅的python》

元类部分

元类是python高阶语法.
合理的使用可以减少大量重复性的代码.

1.1     神奇的Django中的models

我们先来看一段在Django项目中常用的代码:

设置数据库models代码:

class Students(models.Model):
    name = models.CharField()
    age = models.IntegerField()

这里有几个神奇的地方,涉及到了python中最神秘的几个特性。

先看下有哪些神奇的地方:

  • 字段名称nameage自动转换为了数据库中的字段名称
  • 自动校验数据类型,models.IntegerField(),会校验设置的数据类型

这里用的是python的两个语法特性:

  • 描述符协议
  • 元类

我们来一步一步解开神秘面纱。

 

5.2     描述符

某个类,只要是内部定义了方法 __get__, __set__, __delete__
中的一个或多个(set,delete必须有一个),就可以称为描述符。

方法的原型为:

  ① __get__(self, instance, owner)

  ② __set__(self, instance, value)

  ③ __del__(self, instance)

 

描述符只绑定到类上,在实例上不生效。

描述的调用实质为:type(objectA).__dict__[“key”].__get__(None,
objectB),objectB为描述符,objectA为定义类。

 

5.3     元类

元类,就是创建类的类。一般类都继承自object类,默认会创建一些方法。

元类决定了类出初始化后有哪些特征和行为。如果我们想自定义一个类,具备某种特殊的行为,则需要自定义元类。

  • 类也是对象,所有的类都是type的实例
  • 元类(Meta Classes)是类的类
  • __metaclass__ = Meta 是 Meta(name, bases, dict) 的语法糖
  • 可以通过重载元类的 __new__ 方法,修改定义的行为

 

  • 干涉创建类的过程
  • 修改类
  • 返回修改之后的类

1      引子

为什么使用元类?

 

为什么要使用元类这种模糊且容易出错的功能?
一般情况下,我们并不会使用元类,99%的开发者并不会用到元类,所以一般不用考虑这个问题。
元类主用用于创建API,一个典型的例子就是Django的ORM。
它让我们可以这样定义一个类:

 

class Person(models.Model):
  name = models.CharField(max_length=30)
  age = models.IntegerField()

 

运行下面的代码:

guy = Person(name='bob', age='35')
print(guy.age)

返回的结果是int类型而不是IntegerField对象。这是因为models.Model使用了元类,它会将Python中定义的字段转换成数据库中的字段。
通过使用元类,Django将复杂的接口转换成简单的接口。

 

原型:type(类名,基类元组(可以为空,用于继承), 包含属性或函数的字典)

 以下两种写法都可以:

type(‘Class’,(object,),dict(hello=fun()))

type(‘Class’,(object,),{“hello”:fun()})

1、class 自定义的类名称

2、(object,)是继承类,的元组,如果只有一个就写这种形势(object,);多个(object,xxxx,)

3、dict(hello=fun()) 或 {“hello”:fun()}
第三个参数,是一个字典等号左是
自定义的方法名,右侧是已写好的方法名,这个要注意,有参数且没有默认值的情况下,要加括号;

 

def fun():
    print('hello world!')


if __name__=="__main__":

    h = type('Hello',(object,),dict(hello=fun()))
    tc = h()
    tc.hello

 

引用:

h 相当于接收Hello类;tc
= h()实例化类;tc.hello方法,调用的其实是我们定义的fun方法。

    Hello = type('Hello',(object,),dict(hello=fun()))
    tc = Hello()
    tc.hello

 type()动态创建类后,还可以添加更多的方法和属性:

def mysql():
    conn = pymysql.connect(host='127.0.0.1',port=3306 ,user='root' ,passwd='q123456' ,db='amsql' )
    cur = conn.cursor()
    sql = "SELECT * FROM amt_case_interface_table"
    ret = cur.execute(sql)
    print(cur.fetchmany(3))
    #conn.commit()

    cur.close()
    conn.close()

Hello.mysql = mysql()

调用:

tc.mysql

 

Linux and
python学习交流1,2群已满.

Linux and
python学习交流3群新开,欢迎加入,一起学习.qq 3群:563227894

不前进,不倒退,停止的状态是没有的.

一起进步,与君共勉,

 

2      数据校验

 

4.1     版本三

使用元类来控制类的行为:

图片 1图片 2

class NameProperty:
    index = 0

    def __init__(self):
        self.storage_name = str(self.__class__.index)  # 使用类的变量
        self.__class__.index += 1

    def __get__(self, instance, owner):
        return getattr(instance, self.storage_name)

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise TypeError("%s must be string" % self.storage_name)
        instance.__dict__[self.storage_name] = value


class EntityMeta(type):
    def __init__(cls, name, bases, attr_dict):
        super().__init__(name, bases, attr_dict)
        for key, attr in attr_dict.items():
            if isinstance(attr, NameProperty):
                type_name = type(attr).__name__
                attr.storage_name = '{} property {}'.format(type_name, key)


class Student(metaclass=EntityMeta):
    name = NameProperty()
    age = None
    nicky_name = NameProperty()

    def __str__(self):
        return self.name

s = Student()
s.name = "www"
print(s)

s2 = Student()
s2.name = "test"
s2.nicky_name = 4444
print(s2)
print(s2.nicky_name)

View Code

 

执行输出为:

 

raise TypeError("%s must be string" % self.storage_name)

TypeError: NameProperty property nicky_name must be st

 

语法解释:

版本三相比版本二,最大的变化在于Student类继承了自定义元类EntityMeta。

如果对于python面向对象编程有了解的话,python的所有类都继承自type,type是所有类的元类。。

在这里,我们自定义的元类EntityMeta,具备一个功能就是判断类属性是否为NameProperty类型,如果为这个类型,则这个类型的实例属性storage_name值赋值为类名和属性名

 

元类实际上做了以下三方面的工作:

2.1     数据校验难点

Python虽然是强类型的脚本语言,但是在定义变量时却无法指定变量的类型。

例如,我们在Student类中定义一个age字段,合法值一般为包含0的正整数,但是在python中无正整数的类型,只能自己来校验。

class Student:
    def __init__(self, name, age):
        if isinstance(name,str):
            self.name = name
        else:
            raise TypeError("Must be a string")

        if isinstance(int, age):
            self.age = age
        else:
            raise TypeError("Must be an int")

 

但是,如果更新年龄时就会遇到问题,无法重用校验逻辑。

有没有简洁的方法呢?

3      Python描述符

描述符提供了优雅、简洁、健壮和可重用的解决方案。简而言之,一个描述符就是一个对象,该对象代表了一个属性的值。

这就意味着如果一个Student对象有一个属性“name”,那么描述符就是另一个能够用来代表属性“name”持有值的对象。

描述符协议中“定义了__get__”、“__set__”或”__delete__”
这些特殊方法,描述符是实现其中一个或多个方法的对象。

发表评论

电子邮件地址不会被公开。 必填项已用*标注