一、元类
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','商品') """