一、元类
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,xiaoming@demo.com)
# 无法在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='xiaoming@demo.com')
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','xiaoming@demo.com')
"""
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,xiaoming@demo.com)
# 无法在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='xiaoming@demo.com')
u.save()
c = Cate(cid='001', cname="商品")
c.save()
"""
打印结果如下:
insert into User (uid,username,password,email) values (1,'小名同学','admin123','xiaoming@demo.com')
insert into Cate (cid,cname) values ('001','商品')
"""