python笔记6:元类与ORM

Table of Contents

元类

01 全局对象与内嵌模块

02 元类

03 type使用

04 元类应用补充

元类实现ORM

01什么是orm

02实现ORM

03完善对数据类型的检测

04 python操作系统知识回顾

05 复习


元类

01 全局对象与内嵌模块

什么是类?类是一个对象(类对象-创建实例对象的模子对象),用它创建的对象为实例对象

class ObjectCreater(object):

Py中万物皆对象,Linux万物皆文件

Python中有内嵌函数(可以直接调用的函数),如:

input("请输入>>")

print("XXX")

list()

tuple()

dict()

set()

Python运行时默认加载了内建/内嵌模块,其中带有这些常用函数

查看到底有哪些函数可以直接用,globals返回可调用的一个字典:

globals()

新定义一个全局变量:

a=100

再查看globals,会增加一行

'a':100,

新定义一个全局函数:

def AA():

       pass

再查看globals,最新一行会增加

'AA':<function __main__.AA>

新定义一个类:

class BB():

       pass

globals

'BB':__main__.BB

定义类时,会给这个类创建内存空间,globals中的名字指向刚刚创建的对象空间

 

万物皆对象,这个变量也是对象

在字典中不能出现两个相同的key,所以最后导入的、最后赋引用的会覆盖,类、函数、变量不能重名

同理,类的名字指向了这个类对象

类与普通对象的区别只是,它拥有创建其他实例对象空间的能力

但是这个globals中没有print函数,在内嵌模块builtin中。查看这个模块:

xx=globals()#xx指向了这个字典

xx['__builtin__'].__dict__

'input':<function input>

'print':<function print>

调用其中函数

yy=xx['__builtin__'].__dict__#保存这个字典

yy['print']("hah")

输出:hah

 

ipython比单用python时有更多命令可以直接用

把所有输出过的东西存储在Out字典中,把所有输入过的东西存储在In字典中

ipython有一个sqlite数据库,内嵌型(它不需要建立服务器,读取时打开文件)

02 元类

需求-改变类空间内容,如:添加一个属性

类:创建实例对象,实例对象有自己的内存空间,同时也有一部分共享类对象中的数据

元类(python造物主):可以创建类

类也是对象,你可以在运行时创建它们,就像其他任何对象一样。

可以在函数中创建类,使用class关键字即可

>>> def choose_class(name):

       if name == 'foo':

           class Foo(object):

               pass

           return Foo     # 返回的是类,不是类的实例

       else:

           class Bar(object):

               pass

           return Bar

 

>>> MyClass = choose_class('foo')

>>> print(MyClass)  # 函数返回的是类,不是类的实例

<class '__main__'.Foo>

>>> print(MyClass())  # 可以通过这个类创建类实例,也就是对象

<__main__.Foo object at 0x89c6d4c>

比较标准的创建类的方式是type(一般type用来返回一个对象的类型type(100)

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

B = type("B", (), {"num":100,"num2":200})

查看类:

print(help(B))

输出:

num=100

num2=200

Type是一个元类,Type的返回值是一个类对象

字典不一样,则属性不一样

03 type使用

如果元类创建类时需要继承

B = type("B", (), {"num":100,"num2":200})

Test22=type("Test22",(B,),{})#元组中至少要放一个','

查看新类

print(help(Test22))

class Test22(B)

...

num = 100

num2 = 200

有继承的类、属性

添加方法:添加属性时指向方法引用

def test_2(self):

       print(">>实例方法")

Test3=type("Test3",(),{"test_2":test_2})

创建实例对象

t3=Test3()

t3.test_2()

输出:

>>实例方法

添加类方法

#类方法

@classmethod

def test_3(cls):

       print(">>类方法")

Test4=type("Test4",(),{"test_2":test_2,"test_3":test_3})

添加静态方法

#静态方法

@staticmethod

def test_4():

       print(">>静态方法")

Test5=type("Test5",(),{"test_2":test_2,"test_3":test_3,"test_4":test_4})

Demo

class T(object):

       pass

t=T()

t.__class__#查看是哪个类创建的

输出

__main__.T

t.__class__.__class__

输出

type

即使直接写,类还是type创建的

元类创建类对象,类对象创建实例对象

元类的功能:

#-*- coding:utf-8 -*-

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)

 

class Foo(object, metaclass=upper_attr):

    bar = 'bip'

 

print(hasattr(Foo, 'bar'))

print(hasattr(Foo, 'BAR'))

 

f = Foo()

print(f.BAR)

在定义普通类Foo时,如果形如:

class Foo(object):

    bar = 'bip'

就调用默认的Type创建类对象,等价于:

Foo=type("Foo",(object,),{"bar":'bip'})

Python3:添加参数metaclass=upper_attr:不管在类中定义什么属性名,通通变成大写

Python2:

class Foo(object):

    __metaclass__ = upper_attr  # 设置Foo类的元类为upper_attr

不直接调用默认type创建,调用自定义函数创建

自定义函数的返回值:一个类对象

# 调用type来创建一个类

    return type(class_name, class_parents, new_attr)

函数被调用后能像type一样获取类名、父类、类属性,就可以在创建时对他们进行修改后再给type

04 元类应用补充

一般情况不会使用元类

装饰器在不修改原来函数条件下,调用函数前后加新功能

元类不修改原来类定义中内容,但可以修改类属性等

之前一直使用的函数当作元类使用,修改为类:

class UpperAttrMetaClass(type):

    # __new__ 是在__init__之前被调用的特殊方法

    # __new__是用来创建对象并返回的方法

    # 而__init__只是用来将传入的参数初始化给对象

    # 你很少用到__new__,除非希望能够控制对象的创建

    # 这里,创建的对象是类,我们希望能够自定义它,所以改写__new__

    # 还有一些高级的用法会涉及到改写__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

 

        return type(class_name, class_parents, new_attr)

在定义了多个类,想要统一修改某些功能属性时,可以考虑元类

__new__也涉及到单例

Python中没有数组

元类实现ORM

01什么是orm

Django的核心思想:Object Relation Mapping对象与关系映射

创建实例对象,用类名当表名,用类属性当字段,操作实例对象,对应mysql语句

原生Sql方式:

insert into User(id,name) values(123, "laowang")

ORM方式:

u=User(id=123, name="laowang")#创建实例对象

u.save()

02实现ORM

Python多继承时,super按照继承元组的顺序调用,保证被多类继承的那个类只执行一次

#继承type类,是元类

class ModelMetaClass(type):#创建元类对象

       #重写new方法,传入参数name=User,parents=(,),

       #attrs={"uid":(...),"name":(...)}

    def __new__(cls, class_name, class_parents, class_attrs):

 

        mapping = dict()

        #遍历属性元组

        for name, value in class_attrs.items():

               #判断类型是否是指定的Str或Int

            if isinstance(value, tuple):

                print("Found mapping %s --> %s" % (name, value))

                mapping[name] = value#保存在mapping

        #循环mapping中已经保存的字段名

        for name in mapping.keys():

               # 在属性字典中删除

            class_attrs.pop(name)

        #将mapping字典,表名保存在类属性中

        class_attrs["__mapping__"] = mapping

        class_attrs["__table__"] = class_name

        #创建新的类对象

        # 其中属性包括mapping,table表名,没有4个类属性

        return type.__new__(cls, class_name, class_parents, class_attrs)

 

class User(metaclass=ModelMetaClass):#用自定义元类创建user类对象

    uid = ('uid', "int unsigned")

    name = ('username', "varchar(30)")

    email = ('email', "varchar(30)")

    password = ('password', "varchar(30)")

    #创建实例对象后,这些属性没了,变成了mapping字典和table字符串

    # 这样不需要用户自己创建字典,比较简单

    def __init__(self, **kwargs):#创建实例对象时需要传参,以字典的方式保存所有参数

           #拆参数字典

        for name, value in kwargs.items():

               #getattr可获取方法中属性 setattr往实例对象添加属性

               #由于属性名不确定,难以直接用self.XXX=XXX赋值

            setattr(self, name, value)

 

    def save(self):

        fields = []

        args = []

        #self中保存了各个属性

        #mapping字典中保存着表结构"uid属性名":('uid字段名',"unsigned")

        for k, v in self.__mapping__.items():

               #字段名添加在列表中

            fields.append(v[0])

            #拿着key找到实例属性的值,添加到args

            args.append(getattr(self, k, None))

        #插入数据库时需要表名

        # ",".join给字符串链接所有字段名、值,以“,”分隔

        sql = "insert into %s(%s) values(%s)" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))

        print("SQL: %s" % sql)

 

#通过类创建实例对象,会调用__new__,__init__

u = User(uid=123, name="zfx", email="[email protected]", password="6666")

u.save()

定义类属性,操作的时候也操作相同名字的类属性,但对应的在表中保存时,用原来类定义中属性保存下的字段名,不过此时已经保存在mapping中了

03完善对数据类型的检测

当前版本只是连接属性值,当类型为字符串时,没有在sql为它加双引号

sql = "insert into %s(%s) values(%s)" % (self.__table__, ",".join(fields), ",".join([str(i) for i in args]))

改为:

# 整型不用双引号,字符串需要

        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)

使用join时,会剥去外层的引号

此时其他的类也需要定义__init__和save方法,将他们抽象到父类Model中:

class Model(object,metaclass=ModelMetaClass):

    def __init__(self, **kwargs):

        for name, value in kwargs.items():

            setattr(self, name, value)

 

    def save(self):

        fields = []

        args = []

        for k, v in self.__mapping__.items():

            fields.append(v[0])

            args.append(getattr(self, k, None))

        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([str(i) for i in args]))

        print("SQL: %s" % sql)

子类只需继承父类:

class User(Model):

    uid = ('uid', "int unsigned")

    name = ('username', "varchar(30)")

    email = ('email', "varchar(30)")

    password = ('password', "varchar(30)")

真正的django中,字段不是简单的元组,而是对象

04 python操作系统知识回顾

进程之间是独立的(并行,不共享资源),进程间通信:管道(无名管道、命名管道)、消息队列、内存映射、socket。

这些想起以前操作系统的课上学过,作业还用C语言自己实现过

网络:udp、tcp网络进程间通信,不同进程通过端口区分

Tcp的长连接和短连接:长连接-长时间在线服务、可重新利用套接字,短连接-查一下就断开

线程间(多任务)共享资源,进程、线程、协程的区别:

进程(不共享、更稳定)-分配的单位,真正的多任务

线程(共享、更快速)-调度的单位、python默认用GIL,

协程-在线程中有阻塞,消耗时间,此时可调用其他函数,假的多任务

互斥锁-应对资源竞争,可能造成死锁

GIL全局解释器锁-逻辑上同一时刻只有一个线程在执行,但其实在多个切换

计算密集型-进程

网络密集型-携程

正则表达式比较常用,有机会要深化学习

05 复习

http过程主要:

解析域名对应的ip,三次握手连接ip服务器,连接成功发送tcp数据、http协议,返回浏览器显示

其中:

请求交给网关,网关将ip地址变、mac不变放到万维网,

深拷贝、浅拷贝:传一个引用、一个新的地方的引用

私有化:__class__

Import导入模块:import导入可以让变量指向模块中的变量,from再import则不行

面向对象:封装继承多态

多继承与MRO顺序

With与上下文管理器

Mysql视图:虚拟表,数据库可能变化,但代码已经写好

事务A C I D原子性、一致性、隔离性、持久性

索引:使用B-tree记录谁在哪

WSGI协议服务器与web框架间规定

闭包装饰器

前面的python提高部分,正则部分,多任务(线程、进程、协程)都很值得再看

猜你喜欢

转载自blog.csdn.net/lagoon_lala/article/details/106378876