Table of Contents
元类
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提高部分,正则部分,多任务(线程、进程、协程)都很值得再看