Python高级 -- 13 元类、ORM

一、元类


1、理解类也是对象


        Python中的类同样也是一种对象,只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。这个对象(类对象)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象


2、动态的创建类

        因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用class关键字即可。代码如下:


def create_class(name):
    if name == "AAA":
        class AAA(object):
            print("创建AAA类")
        return AAA
    elif name == "BBB":
        class BBB(object):
            print("创建BBB类")
        return BBB


# 调用create_class方法,并传递参数
# 想要创建AAA对象,需要传递对应的参数
AAA_class = create_class("AAA")
print(AAA_class)

# 可以通过AAA_class这个类创建示例对象
print(AAA_class())

"""
    打印结果:

        创建AAA类
        <class '__main__.create_class.<locals>.AAA'>
        <__main__.create_class.<locals>.AAA object at 0x000002235BBBA860>

"""


        但这还不够动态,因为你仍然需要自己编写整个类的代码。由于类也是对象,所以它们必须是通过什么东西来生成的才对。当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。此时需要用到Python的内置函数:type


        type不仅仅可以返回一个对象的类型,同时,还可以用来创建一个类,即type就是元类(元类可以创建元类,元类也可以创建其他所有的类)


3、使用type创建类


        type还有一种完全不同的功能,动态的创建类。


        type可以接受一个类的描述作为参数,然后返回一个类。(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)



type创建类的语法:


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


# 使用class关键词定义一个类
class Test:
    pass 

# 以上使用关键词定义一个类的方式,可以是用type如下定义
# 类名叫做Test2,父类为空,属性为空
Test2 = type("Test2", (), {})


使用help测试两个类,结果如下:


help(Test)
"""
	Help on class Test in module __main__:

	class Test(builtins.object)
	 |  # 使用class关键词定义一个类
	 |
	 |  Data descriptors defined here:
	 |
	 |  __dict__
	 |      dictionary for instance variables (if defined)
	 |
	 |  __weakref__
	 |      list of weak references to the object (if defined)
"""


help(Test2)
"""
	Help on class Test2 in module __main__:

	class Test2(builtins.object)
	 |  Data descriptors defined here:
	 |  
	 |  __dict__
	 |      dictionary for instance variables (if defined)
	 |  
	 |  __weakref__
	 |      list of weak references to the object (if defined)
"""


4、使用type创建带有属性的类


# 使用type创建一个带有属性的类
# 类名叫做Test,继承自object,有一个属性为bar=True
Test = type("Test", (object, ), {"bar" : True})

"""
    以上可以翻译为:
        class Test(object):
            bar = True
"""

# 创建一个子类,继承Test
Child_Class = type("Child_Class", (Test,), {})


help(Test)
"""
    Help on class Test in module __main__:

    class Test(builtins.object)
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |  
     |  bar = True
"""


help(Child_Class)
"""
    class Child_Class(Test)
     |  Method resolution order:
     |      Child_Class
     |      Test
     |      builtins.object
     |  
     |  Data descriptors inherited from Test:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes inherited from Test:
     |  
     |  bar = True
"""


注意:type的第2个参数,元组中是父类的名字,而不是字符串;添加的属性是类属性,并不是实例属性


5、使用type创建带有方法的类


(1)、添加实例方法


# 使用type创建一个带有属性的类
# 类名叫做Test,继承自object,有一个属性为bar=True
Test = type("Test", (object, ), {"bar" : True})

"""
    以上可以翻译为:
        class Test(object):
            bar = True
"""

# 创建一个子类,继承Test
Child_Class = type("Child_Class", (Test,), {})


# 定义一个普通的函数
def func1(self):
    print("这是一个方法:", self.bar)

# 使用type创建一个类,让func1指向上面定义的方法
Test1 = type("Test1", (Child_Class, ), {"func1": func1})

# 使用hasattr方法判断Test1中是否有func1这个属性
print(hasattr(Test1, "func1"))  # True
help(Test1)
"""
    Help on class Test1 in module __main__:

    class Test1(Child_Class)
     |  Method resolution order:
     |      Test1
     |      Child_Class
     |      Test
     |      builtins.object
     |  
     |  Methods defined here:
     |  
     |  func1(self)
     |      # 定义一个普通的函数
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from Test:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes inherited from Test:
     |  
     |  bar = True
"""

# 使用hasattr方法判断Child_Class中是否有func1这个属性
print(hasattr(Child_Class, "func1"))    # False


(2)、添加静态方法


# 使用type创建一个带有属性的类
# 类名叫做Test,继承自object,有一个属性为bar=True
Test = type("Test", (object, ), {"bar" : True})

"""
    以上可以翻译为:
        class Test(object):
            bar = True
"""

# 创建一个子类,继承Test
Child_Class = type("Child_Class", (Test,), {})

# 定义一个普通的函数
def func1(self):
    print("这是一个方法:", self.bar)

# 定义一个静态方法
@staticmethod
def test_static():
    print("这是一个静态方法")

Test1 = type("Test1", (Child_Class, ), {"func1": func1, "test_static": test_static})

test1 = Test1()
test1.test_static()     # 这是一个静态方法
test1.func1()           # 这是一个方法: True


(3)、添加类方法


# 使用type创建一个带有属性的类
# 类名叫做Test,继承自object,有一个属性为bar=True
Test = type("Test", (object, ), {"bar" : True})

"""
    以上可以翻译为:
        class Test(object):
            bar = True
"""

# 创建一个子类,继承Test
Child_Class = type("Child_Class", (Test,), {})

# 定义一个普通的函数
def func1(self):
    print("这是一个方法:", self.bar)

# 定义一个静态方法
@staticmethod
def test_static():
    print("这是一个静态方法")

# 定义一个类方法
@classmethod
def test_classmethod(cls):
    print(cls.bar)

Test1 = type("Test1", (Child_Class, ), {"func1": func1, "test_static": test_static, "test_classmethod": test_classmethod})

test1 = Test1()
test1.test_classmethod()           # True


较为完整的使用type创建类的方式:


class A(object):
    num = 100

def print_b(self):
    print(self.num)

@staticmethod
def print_static():
    print("----haha-----")

@classmethod
def print_class(cls):
    print(cls.num)

B = type("B", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class})
b = B()
b.print_b()
b.print_static()
b.print_class()
""" 结果
    100
    ----haha-----
    100  """


6、什么是元类


        元类就是用来创建这些类(对象)的,元类就是类的类;函数type实际上是一个元类,type就是Python在背后用来创建所有类的元类;Python中所有的东西(对象),包括整数、字符串、函数以及类,它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。


7、__methodclass__属性(python2中的用法)


在定义一个类的时候为其添加__metaclass__属性。如下:


class Foo(object):
    __metaclass__ = 一个类的引用
    ...


        Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下class Foo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。


当写了一个普通的创建类的方法时,代码如下:


class Foo():
    pass


Python做了如下的操作:


        Foo中有__metaclass__这个属性吗?如果是,Python会通过__metaclass__创建一个名字为Foo的类(对象)


        如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。


        如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。


        如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。


8、自定义元类


自定义元类的主要目的就是为了当创建类时能够主动的改变类。


使用函数来当作元类:


# 自定义一个将所有属性都变成大写字母的方法
# class_name : 类名
# class_parents : 父类元组
# class_attr : 属性字典
def upper_attr(class_name, class_parents, class_attr):

    #遍历属性字典,把不是__开头的属性名字变为大写
    new_attr = {}
    for name,value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    #调用type来创建一个类
    return type(class_name, class_parents, new_attr)

# 如果定义类的属性中有metaclass这个属性,则会自动使用metaclass对应的方法去创建当前的类
class Foo(object, metaclass=upper_attr):
    bar = 'bip'

print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)


使用类当作元类:


class UpperAttrMetaClass(type):
    # __new__ 是在__init__之前被调用的特殊方法
    # __new__是用来创建对象并返回之的方法
    # 而__init__只是用来将传入的参数初始化给对象
    # 你很少用到__new__,除非你希望能够控制对象的创建
    # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
    # 如果你希望的话,你也可以在__init__中做些事情
    # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
    def __new__(cls, class_name, class_parents, class_attr):
        # 遍历属性字典,把不是__开头的属性名字变为大写
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法1:通过'type'来做类对象的创建
        return type(class_name, class_parents, new_attr)

        # 方法2:复用type.__new__方法
        # 这就是基本的OOP编程,没什么魔法
        # return type.__new__(cls, class_name, class_parents, new_attr)

# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
    bar = 'bip'

# python2的用法
# class Foo(object):
#     __metaclass__ = UpperAttrMetaClass
#     bar = 'bip'


print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True

f = Foo()
print(f.BAR)
# 输出:'bip'


9、元类总结


        就元类本身而言,它们的作用很简单,如下:拦截类的创建修改类返回修改之后的类


        "元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。" —— Python界的领袖 Tim Peters


二、ORM


1、ORM是什么


        ORM 是 python编程语言后端web框架 Django的核心思想,“Object Relational Mapping”,即对象-关系映射,简称ORM。


        一个句话理解就是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句。


2、通过元类实现简单的ORM中的insert功能


# 创建一个元类,一个类继承了type,就是元类
class ModelMetaClass(type):
    # cls:类对象   name:类名   bases:父类元组  attrs:属性字典
    def __new__(cls, name, bases, attrs):
        mapping = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            # 即:判断v是否是一个元组
            if isinstance(v, tuple):
                print("Found mapping : %s ==> %s" %(k, v))
                # 将k 和 v 放到mapping中
                mapping[k] = v

        # 根据mapping中的key删除attrs中的对应的属性字典
        for k in mapping.keys():
            attrs.pop(k)

        # 将之前的属性和字段对应关系{"uid":('uid','int unsigned'),...}放入属性字典中
        attrs["__mappings__"] = mapping     # 保存属性和列的映射关系
        attrs["__table__"] = name   # 让表名和类名一致
        # 使用type创建类
        return type.__new__(cls, name, bases, attrs)


# 实体类
class User(metaclass = ModelMetaClass):
    uid = ("uid", "int unsigned")
    name = ("username", "varchar(20)")
    password = ("password", "varchar(30)")
    email = ("email", "varchar(40)")

    """
        当实体类使用ModelMetaClass这个元类创建实例对象之后,以上的属性就相当于变为如下属性:

        __mappings__ = {
            "uid":("uid", "int unsigned"),
            "name":("username", "varchar(20)"),
            "password":("password", "varchar(30)"),
            "email":("email", "varchar(40)")
        }
        __table__ = "User"

    """
    def __init__(self, **kwargs):
        # 创建实例对象的时候,kwargs 为: uid = xxx , name = xxx , password = xxx , email = xxx  
        for name, value in kwargs.items():
            # 将kwagrs中没个对应的参数名和value放入实例对象的属性和对应的值中
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0]) # 创建字段名称列表
            args.append(getattr(self, k, None)) # 根据k(uid)获取实例对象中属性的值,并放入参数列表中

        # 编写sql, ",".join(列表):该方法将列表中的没个元素取出,并用,号隔开,并返回字符串
        # sql = """insert into %s (%s) values (%s)""" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))
        # 以上写sql的方法会出现数据类型错误的问题,即生成的sql如下:
        # insert into User (uid,username,password,email) values (1,小名同学,admin123,[email protected])
        # 无法在mysql中直接运行
        args_temp = list()
        for temp in args:
            # 判断数据类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        sql = """insert into %s (%s) values (%s)""" % (self.__table__, ",".join(fields), ",".join(args_temp))
        print(sql)


# 实践以上代码
u = User(uid=1, name='小名同学', password='admin123', email='[email protected]')
u.save()

"""
    打印结果如下:

        Found mapping : uid ==> ('uid', 'int unsigned')
        Found mapping : name ==> ('username', 'varchar(20)')
        Found mapping : password ==> ('password', 'varchar(30)')
        Found mapping : email ==> ('email', 'varchar(40)')
        insert into User (uid,username,password,email) values (1,'小名同学','admin123','[email protected]')

"""


3、将类中除了定义属性的其他方法抽取到父类中


# 创建一个元类,一个类继承了type,就是元类
class ModelMetaClass(type):
    # cls:类对象   name:类名   bases:父类元组  attrs:属性字典
    def __new__(cls, name, bases, attrs):
        mapping = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            # 即:判断v是否是一个元组
            if isinstance(v, tuple):
                print("Found mapping : %s ==> %s" %(k, v))
                # 将k 和 v 放到mapping中
                mapping[k] = v

        # 根据mapping中的key删除attrs中的对应的属性字典
        for k in mapping.keys():
            attrs.pop(k)

        # 将之前的属性和字段对应关系{"uid":('uid','int unsigned'),...}放入属性字典中
        attrs["__mappings__"] = mapping     # 保存属性和列的映射关系
        attrs["__table__"] = name   # 让表名和类名一致
        # 使用type创建类
        return type.__new__(cls, name, bases, attrs)

# 定义基类
class Model(object, metaclass=ModelMetaClass):
    def __init__(self, **kwargs):
        # 创建实例对象的时候,kwargs 为: uid = xxx , name = xxx , password = xxx , email = xxx  
        for name, value in kwargs.items():
            # 将kwagrs中没个对应的参数名和value放入实例对象的属性和对应的值中
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0]) # 创建字段名称列表
            args.append(getattr(self, k, None)) # 根据k(uid)获取实例对象中属性的值,并放入参数列表中

        # 编写sql, ",".join(列表):该方法将列表中的没个元素取出,并用,号隔开,并返回字符串
        # sql = """insert into %s (%s) values (%s)""" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))
        # 以上写sql的方法会出现数据类型错误的问题,即生成的sql如下:
        # insert into User (uid,username,password,email) values (1,小名同学,admin123,[email protected])
        # 无法在mysql中直接运行
        args_temp = list()
        for temp in args:
            # 判断数据类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        sql = """insert into %s (%s) values (%s)""" % (self.__table__, ",".join(fields), ",".join(args_temp))
        print(sql)



# 实体类
class User(Model):
    uid = ("uid", "int unsigned")
    name = ("username", "varchar(20)")
    password = ("password", "varchar(30)")
    email = ("email", "varchar(40)")

    """
        当实体类使用ModelMetaClass这个元类创建实例对象之后,以上的属性就相当于变为如下属性:

        __mappings__ = {
            "uid":("uid", "int unsigned"),
            "name":("username", "varchar(20)"),
            "password":("password", "varchar(30)"),
            "email":("email", "varchar(40)")
        }
        __table__ = "User"

    """

class Cate(Model):
    cid = ("cid", "varchar(10)")
    cname = ("cname", "varchar(30")
   

# 实践以上代码
u = User(uid=1, name='小名同学', password='admin123', email='[email protected]')
u.save()

c = Cate(cid='001', cname="商品")
c.save()

"""
    打印结果如下:

        insert into User (uid,username,password,email) values (1,'小名同学','admin123','[email protected]')

        insert into Cate (cid,cname) values ('001','商品')

"""


猜你喜欢

转载自blog.csdn.net/wingzhezhe/article/details/79700981
今日推荐