面向对象三大特性
- 继承
- super作用:执行mro的下一个类中的相关方法
例
class Base:
def func(self):
print('base.func')
super().func() # 执行mro的下一个类中的func
class Foo:
def func(self):
print('foo.func')
class Bar(Base, Foo):
pass
o = Bar()
o.func()
运行结果
base.func
foo.func
图示
- self到底是谁
如果一个类a,继承了另外一个类b,这个类还继承了一个类c,如果a中有个self.方法d,有些在a中找,如果a没有就去b找,b没有去c找,再c中找到此方法d后,该方法中有self.方法f,又要回到最初的a中找。
- 封装
- 将一些函数封装到一个类中,就是面向对象的思想。
如一个py文件中有50个函数,由于很乱,所以分门别类整理好,找的时候比较方便。
class DB():与DB相关的方法,class Cache:与缓存相关的方法 - 将一些数据封装到一个对象中
有些接口返回给用户json数据,一个字典,再接口中创建一个字典,再返回给用户数据获取与否的状态等。但如果要写20个接口,对应20个函数,意味着再每个函数中都定义这个字典,在重复过程中容易出错,所以把这个字典封装到一个对象中。实例化这个类,引用这个对象的数据,相对于前者比较简介且标准化
两者方法对比:
应用场景:
DRF:requset即封装了Django的request又进一步封装了DRF的相关内容。
- 多态
python本身就是多态(鸭子模型),
如图:arg既可以传o,可以传b,只要能send
对比java的多态:
def func(string: arg): # arg指定了只能传string
arg.send()
# java的多态局限性:arg可以是string对象也可以是string的派生类的对象。
# 而python中可以是任意对象
-
面向对象的应用场景
- drf的视图,写接口时继承的视图类:apiview,modelviewside等等
- drf中request的封装
-
面向对象中classmethod和staticmethod区别
@classmethod
@staticmethod
共同点:都通过类调用,不用通过对象就可调用,staticmethod可以没有参数,classmethod至少有个个参数:类。 -
面向对象的成员修饰符
- 公有
- 私有:双下滑线表示私有,只能自己类来调用,别的类调用不了。包括派生类(继承的类)
-
面向对象的成员
class Foo:
a = 123 # 类变量/静态字段、静态数据
def __init__(self):
self.name = 'Lau' # 实例变量/字段/属性
def func(self): # 方法/绑定方法:绑定到类中的方法
pass
@classmethod
def func(cls): # 类方法
pass
@staticmethod
def func(): # 静态方法
pass
@property
def json(self): # 属性
return ...
- 对象和对象之间能否相加
一般不能相加,如:
class F:
pass
class B:
pass
o1 = F()
o2 = B()
r = o1 + o2
运行结果
r = o1 + o2
TypeError: unsupported operand type(s) for +: 'F' and 'B'
增加__add__方法
class F:
def __init__(self, num):
self.num = num
def __add__(self, other):
return self.num + other.num
class B:
def __init__(self, num):
self.num = num
o1 = F(2)
o2 = B(4)
r = o1 + o2 # 执行o1中的__add__方法,把后面对象当做参数传进去,
print(r) # >>>6
- 面向对象特殊方法
- 双下滑线
- _dict_
- _call_:对象加括号执行其__call__方法,flask,django请求进来的入口都是在__call__这个方法中,一个web框架首先要做两步:准备工作:运行socket到recv等待请求到来,到来时立即执行操作,这步都是从__call__方法开始的
图示
flask:
一旦请求进来,run_simple中的第三个参数self就会加括号,也就是对象加括号执行这个对象的__call__方法
django:
有些框架如果依赖wsgi,请求入口都比较相似。
- _new_
在__init__初始化之前,__new__返回什么,这个对象就是什么,构造方法
class F(object):
pass
o = F()
print(o) # >>> <__main__.F object at 0x000001D4ED79E390>
class F:
def __new__(cls, *args, **kwargs):
return 1
o = F()
print(o) # >>> 1
所以实例化某些对象时,有时对象的结果是不一样的,如DRF中的序列化时传的参数不同,对象的值不同
如:
- 单例模式
设计模式的一种,实例化一个对象,无论什么时候实例化都是这个对象,
基本流程:
class Singleton:
instance = None
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = super().__new__(cls)
return cls.instance
obj1 = Singleton()
print(obj1)
obj2 = Singleton()
print(obj2)
运行结果
<__main__.Singleton object at 0x000002B73FBBB9E8>
<__main__.Singleton object at 0x000002B73FBBB9E8>
两者内存地址一样
但是单例模式不加锁时错误的,在多进程或多线程中能体现出来,
如:
import threading
import time
class Singleton:
instance = None
def __new__(cls, *args, **kwargs):
if not cls.instance:
time.sleep(5)
cls.instance = super().__new__(cls)
return cls.instance
def task(a):
o = Singleton()
print(o)
for i in range(10):
t = threading.Thread(target=task, args=(i,))
t.start()
运行结果:
<__main__.Singleton object at 0x000002F0170DB9E8>
<__main__.Singleton object at 0x000002F017299898>
<__main__.Singleton object at 0x000002F0172BF4A8>
<__main__.Singleton object at 0x000002F0172A23C8>
<__main__.Singleton object at 0x000002F0172AE780>
<__main__.Singleton object at 0x000002F0172CA0F0>
<__main__.Singleton object at 0x000002F01730A978>
<__main__.Singleton object at 0x000002F0172CA0F0>
<__main__.Singleton object at 0x000002F01730A128>
<__main__.Singleton object at 0x000002F0172CA0F0>
由于创建对象时一起创建的,所以每个结果不一样,可以加锁来控制
import threading
import time
class Singleton:
instance = None
lock = threading.RLock()
def __new__(cls, *args, **kwargs):
with cls.lock:
if not cls.instance:
time.sleep(5)
cls.instance = super().__new__(cls)
return cls.instance
def task(a):
o = Singleton()
print(o)
for i in range(10):
t = threading.Thread(target=task, args=(i,))
t.start()
运行结果
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
<__main__.Singleton object at 0x000001C4AD2097F0>
但是加锁和解锁浪费资源,为了优化代码性能,在对象已经存在的情况下直接返回该对象
代码更改如下:
import threading
import time
class Singleton:
instance = None
lock = threading.RLock()
def __new__(cls, *args, **kwargs):
if cls.instance:
return cls.instance
with cls.lock:
if not cls.instance:
time.sleep(5)
cls.instance = super().__new__(cls)
return cls.instance
def task(a):
o = Singleton()
print(o)
for i in range(10):
t = threading.Thread(target=task, args=(i,))
t.start()
- item
_getitem_
_setitem_
_delitem_
Django中的session存取值session[’’] = value,一个对象加中括号,并传值,
class Session:
pass
obj = Session()
obj['x1'] = 123
运行结果
obj['x1'] = 123
TypeError: 'Session' object does not support item assignment
要使得其支持以上操作,就要定义__setitem__方法, 类似+触发__add__方法一样,
class Session:
def __setitem__(self, key, value):
print(key, value)
def __getitem__(self, item):
print(item)
def __delitem__(self, key):
print(key)
obj = Session()
obj['x1'] = 123
obj['x1']
del obj['x1']
运行结果
x1 123
x1
x1
Django源码:
- _iter_ 有此方法,并返回一个迭代器, 就是可迭代对象
- 面向对象上下文管理
-_enter_
-_exit_
class Foo():
def __enter__(self):
print('进入')
return 333
def __exit__(self, exc_type, exc_val, exc_tb):
print('退出')
obj = Foo()
with obj as x: # __enter__返回什么,x就是什么
print(x)
print('操作中...') # 执行完其中的代码,自动执行__exit__退出
运行结果
进入
333
操作中...
退出
应用场景:
文件句柄,lock,数据库的连接和断开,flask的localstack对象的上下文管理
- 队列
类似管道,放一个取走一个,先进先出 - 栈
后进先出,先进后出
弹夹最后一个先打出去
代码
class Stack:
def __init__(self):
self.container = []
def push(self, value):
'''
向栈中插入数据
:param value:
:return:
'''
self.container.append(value)
def pop(self):
'''
从栈中取走数据
:return:
'''
return self.container.pop()
应用场景:Scrapy框架中,Flask源码的LocalStack对象维护的一个栈
如:
- 面向对象的元类metaclass
对象是类()创建的
类是type()创建的,一般定义一个类就写出来了一个类,
但类其实有两种创建方式,平时只用一种:class 类…,另外一种写法:
# class Foo():
# name = 'Lau'
#
# def func(self):
# return 123
Foo = type('Foo', (object, ), {'name': 'Lau', 'func': lambda self:123}) # 和以上创建类效果一样,类是type()创建的
obj = Foo()
ret = obj.func()
print(ret) # >>> 123
只要是class 类名,一定调用了type(),type()又调用object(),type是由object创建的,object是由C创建的,C语言底层的代码开辟了空间,所以object是python类的最底层的代码,主要是用来在内存中开辟一个空间等。
- metaclass指定类由哪个type创建(继承type的子类),默认为type。
class MyType(type):
def __init__(self, *args, **kwargs):
print(args)
super().__init__(*args, **kwargs)
class Foo(object, metaclass=MyType): # 实例化时调用了MyType
name = 'Lau'
def func(self):
return 123
运行结果
('Foo', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'Foo', 'name': 'Lau', 'func': <function Foo.func at 0x0000026FA3294840>})
# 类名, 继承, 成员
- 如果类指定了metaclass为MyType,派生类都是又指定的类MyType创建
class MyType(type):
def __init__(self, *args, **kwargs):
print(args)
super().__init__(*args, **kwargs)
class Foo(metaclass=MyType):
pass
class Bar(Foo): # 由于Bar的基类是由MyType创建,所以Bar也是由MyType创建
pass
运行结果
('Foo', (), {'__module__': '__main__', '__qualname__': 'Foo'})
('Bar', (<class '__main__.Foo'>,), {'__module__': '__main__', '__qualname__': 'Bar'})
指定metaclass的源码:
- flask中的wtforms
上图关系:
Form的基类是NewBase,NewBase的基类是BaseForm,且由FormMeta创建,FormMeta本身继承type,所以Form也是由FormMeta创建的。
为什么要用FormMeta创建呢?因为FormMeta中做了一些顺序的操作,内部创建了一个计数器,在代码编译过程中,实例化每个字段时。计数器记录了谁是第一个,谁是第二个, Django1.10前没有这个功能,以便循环时每个字段展示的顺序是代码本来的顺序。
指定metaclass的作用,在类初始化时可以做一些操作,运行脚本时就执行了,类加载时自定义一些代码执行。不用等到实例化时才做。
- Django的form组件
from django import forms
class LoginForm(forms.Form):
name = forms.CharField()
总结:
当一个对象实例化时,执行了创建该对象的__call__方法,当创建一个类时,由于实际上执行了创建该类的类加括号,也就是__call__方法,而该方法中执行了__init__和__new__方法。
如:
class MyType(type):
def __init__(self, name, bases, dic): # self: Foo,类的初始化,
print('type.init')
super().__init__(name, bases, dic)
def __new__(cls, name, bases, dic): # 用来开辟空间,创建类,要有返回值,否则会空,cls--->MyType,MyType创建的,创建时的参数要传进去。返回什么其实例化对象就是什么, 如返回123,其对象就是123
print('type.new')
return super().__new__(cls, name, bases, dic)# 创建了类并赋值给Foo
def __call__(self, *args, **kwargs): # self是Foo, 由于对象加()执行的是__call__方法,本质上__new__方法和__init__方法是__call__触发的
print(self, args, kwargs)
print('创建对象')
ob = self.__new__(self, *args, **kwargs)
ob.__init__(*args, **kwargs)
print(ob)
class Foo(object, metaclass=MyType): # 执行MyType(), 执行MyType的__call__方法(对象Foo调用),由于没有,执行父类type的__call__方法,其__call__方法中执行__new__()(对象Foo调用),__init__()(对象Foo调用)
def __init__(self): # 如果该方法没有在创建该类的__call__方法中执行,则不会生效。
print('init')
def __new__(cls, *args, **kwargs):
print(1)
return super().__new__(cls)
obj = Foo()
运行结果:
type.new
type.init
<class '__main__.Foo'> () {}
创建对象
1
init
<__main__.Foo object at 0x000001A1E018E390>
应用场景:
在类创建前写条日志,类实例化后再写入一条日志,可以在MyType的_init_,_new_,或__call__方法中插入代码。
- metaclass 相关补充
class Foo:
def func(self):
print('foo.func')
obj = Foo()
obj.func() # 一般的执行类中的方法方式, 被对象调用时叫方法
Foo.func(obj) # self默认为类的对象。如果类直接调用其方法时,self没有创建过,可手动传,类调用时叫函数
运行结果
foo.func
foo.func
一般平时不用metaclass,在源码中出现的比较