类属性和类对象
类属性字段在内存中只保存一份
对象属性每个对象都保存一份
类也是对象
作用域
• _name 人为约定不可修改
• __name 私有属性
• __name__ 魔术方法
类名.__dict__ #可以查看定义的字段 有静态字段
对象.__dict__#普通字段
dir(类名/对象名)#列表形式返回字段
setattr(list,'新属性名字','新属性.value')#给类增加属性和值,但是内置类型不能增加属性和方法
().__class__.__bases__[0].__subclasses__()#子类
>>> ().__class__
<class 'tuple'>
>>> ().__class__.__bases__#父类
(<class 'object'>,)
>>> ().__class__.__bases__[0]#取出
<class 'object'>
>>> ().__class__.__bases__[0].__subclasses__()
三种方法
• 普通方法 至少一个 self 参数,表示该方法的对象
• 类方法 至少一个 cls 参数,表示该方法的类@classmethod(语法糖)
• 静态方法 由类调用,无参数, 有关系但是不引用类的属性@staticmethod
三种方法在内存中都归属于类
特殊属性与方法
• __init__()初始化函数
构造函数是__new__()
__init__() 方法所做的工作是在类的对象创建好之后进行变量的初始化。
__init__() 方法不需要显式返回,默认为 None,否则会在运行时抛出 TypeError。
• self
self 表示实例对象本身
self 不是 Python 的关键字(cls也不是),可以将 self 替换成任何你喜欢的名称,如 this、obj 等,实际效果和 self 是一样的(不推荐)。
在方法声明时,需要定义 self 作为第一个参数,调用方法的时候不用传入 self。
在类中,需要对实例获取属性这一行为进行操作,可以使用:
• __getattribute__() 显式定义可以覆盖
• __getattr__()
异同:
都可以对实例属性进行获取拦截
__getattr__() 适用于未定义的属性 属性不在dict实例中
__getattribute__() 对所有属性的访问都会调用该方法
#如果同时存在,执行的顺序是:
__getattribute__>__getattr__>__dict__
class Human2(object):
def __getattribute__(self, item):
"""
将不存在的属性设置为100并返回,模拟getattr行为
"""
print('Human2:__getattribute__')
try:
return super().__getattribute__(item)
except Exception as e:
self.__dict__[item] = 100
return 100
h1 = Human2()
print(h1.noattr)
******************************************
class Human2(object):
"""
属性不在实例的__dict__中,__getattr__被调用
"""
def __init__(self):
self.age = 18
def __getattr__(self, item):
print(f' __getattr__ called item:{item}')
# 不存在的属性返回默认值 'OK'
return 'OK'
h1 = Human2()
print(h1.age)
print(h1.noattr)
属性描述符 property
描述符:实现特定协议的类
property 类需要实现 get、set、 delete 方法
class Teacher:
def init(self, name):
self.name = name
def get(self):
return self.name
def set(self, value):
self.name = value
pythonteacher = Teacher(‘yin’)
pythonteacher.name = ‘wilson’
print(pythonteacher.name)
描述符:实现特定协议的类
property 类需要实现 __get__、__set__、__delete__ 方法
class Desc(object):
"""
通过打印来展示描述器的访问流程
"""
def __init__(self, name):
self.name = name
#instance当前的类是通过哪个类做的实例化 owner实例所属于的类
def __get__(self, instance, owner):
print(f'__get__{instance} {owner}')
return self.name
def __set__(self, instance, value):
print(f'__set__{instance} {value}')
self.name = value
def __delete__(self, instance):
print(f'__delete__{instance}')
del self.name
property将方法封装为属性
class Human(object):
def __init__(self, name):
self.name = name
# 将方法封装成属性
@property
def gender(self):
return 'M'
h1 = Human('Adam')
h2 = Human('Eve')
h1.gender
# AttributeError:
h2.gender = 'F'
#################
# GOD
class Human2(object):
def __init__(self):
self._gender = None
# 将方法封装成属性
@property
def gender2(self):
print(self._gender)
# 支持修改
@gender2.setter
def gender2(self,value):
self._gender = value
# 支持删除
@gender2.deleter
def gender2(self):
del self._gender
h = Human2()
h.gender2 = 'F'
h.gender2
del h.gender2
# 另一种property写法
# gender = property(get_, set_, del_, 'other property')
# 被装饰函数建议使用相同的gender2
# 使用setter 并不能真正意义上实现无法写入,gender被改名为 _Article__gender
# property本质并不是函数,而是特殊类(实现了数据描述符的类)
# 如果一个对象同时定义了__get__()和__set__()方法,则称为数据描述符,
# 如果仅定义了__get__()方法,则称为非数据描述符 getAttribute
# property的优点:
# 1 代码更简洁,可读性、可维护性更强。
# 2 更好的管理属性的访问。
# 3 控制属性访问权限,提高数据安全性。
# ###########################property 纯python实现
class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
property用法
#####################真u昂太判断
class Node(db.Model):
id = db.Column(db.Integer, primary_key=True)
updated_at = db.Column(db.DateTime) # 节点最后心跳时间
state = db.Column(db.Integer, nullable=False) # 节点是否禁用
@property
def is_active(self):
if(datetime.datetime.now() - self.updated_at).secondes > 60 \
and self.vm_state == 0:
return False
return True
###################密码输入############
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), nullable=False, unique=True)
password_hash = db.Column(db.String(255), nullable=False)
# #########使用装饰器完成password的读取和写入功能分离
@property
def password(self):
return None
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
def is_active(self):
return True
面向对象编程的特性
封装
• 将内容封装到某处
• 从某处调用被封装的内容
继承
• 基本继承
• 多重继承
重载
• Python 无法在语法层面实现数据类型重载,需要在代码逻辑上实现
• Python 可以实现参数个数重载
多态
• Pyhon 不支持 Java 和 C# 这一类强类型语言中多态的写法,
• Python 使用“鸭子类型”
新式类
新式类和经典类的区别
• 当前类或者父类继承了 object 类,那么该类便是新式类,否则便是经典类
object 和 type 的关系
• object 和 type 都属于 type 类 (class ‘type’) 一切皆对象
• type 类由 type 元类自身创建的。object 类是由元类 type 创建
• object 的父类为空,没有继承任何类
• type 的父类为 object 类 (class ‘object’)
类的继承
• 单一继承
• 多重继承
• 菱形继承(钻石继承)
• 继承机制 MRO
• MRO 的 C3 算法
# 钻石继承
class BaseClass(object):
num_base_calls = 0
def call_me(self):
print ("Calling method on Base Class")
self.num_base_calls += 1
class LeftSubclass(BaseClass):
num_left_calls = 0
def call_me(self):
print ("Calling method on Left Subclass")
self.num_left_calls += 1
class RightSubclass(object):
num_right_calls = 0
def call_me(self):
print("Calling method on Right Subclass")
self.num_right_calls += 1
class Subclass(LeftSubclass,RightSubclass):
pass
a = Subclass()
a.call_me()
print(Subclass.mro())
# 广度优先, 另外Python3 中不加(object)也是新式类,但是为了代码不会误运行在python2下产生意外结果,仍然建议增加
# >>> Subclass.mro()#可以分析钻石继承
# [<class '__main__.Subclass'>, <class '__main__.LeftSubclass'>, <class '__main__.RightSubclass'>, <class '__main__.BaseClass'>, <class 'object'>]
# 修改RightSubclass 的 父类为 Object
# >>> Subclass.mro()
# [<class '__main__.Subclass'>, <class '__main__.LeftSubclass'>, <class '__main__.BaseClass'>, <class '__main__.RightSubclass'>, <class 'object'>]
多重继承的顺序问题
有向无环图:DAG(Directed Acyclic Graph) • DAG 原本是一种数据结构,因为 DAG 的拓扑结构带来的优异特性,经常被用于处理动态规划、寻求最短路径的场景。
SOLID 设计原则
• 单一责任原则 The Single Responsibility Principle
• 开放封闭原则 The Open Closed Principle
• 里氏替换原则 The Liskov Substitution Principle
• 依赖倒置原则 The Dependency Inversion Principle
• 接口分离原则 The Interface Segregation Principle
单例模式
- 对象只存在一个实例
- __init__ 和__new__ 的区别:
• __new__ 是实例创建之前被调用,返回该实例对象,是静态方法
• __init__ 是实例对象创建完成后被调用,是实例方法
• __new__ 先被调用,__init__ 后被调用
• __new__ 的返回值(实例)将传递给 __init__ 方法的第一个参数,__init__ 给这个
实例设置相关参数
单例模式三种实现方式:
# ###########装饰器实现单实例模式
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
pass
m1 = MyClass()
m2 = MyClass()
print(id(m1))
print(id(m2))
############# __new__ 与 __init__ 的关系#########
class Foo(object):
def __new__(cls, name):
print('trace __new__')
return super().__new__(cls)
def __init__(self, name):
print('trace __init__')
super().__init__()
self.name = name
bar = Foo('test')
bar.name
#相当于在执行下面的操作
bar = Foo.__new__(Foo, 'test')
if isinstance(bar, Foo):
Foo.__init__(bar, 'test')
############################
# __new__ 方式实现单例模式#######################
class Singleton2(object):
__isinstance = False # 默认没有被实例化
def __new__(cls, *args, **kwargs):
if cls.__isinstance:
return cls.__isinstance # 返回实例化对象
cls.__isinstance = object.__new__(cls) # 实例化
return cls.__isinstance
# object定义了一个名为Singleton的单例,它满足单例的3个需求:
# 一是只能有一个实例;
# 二是它必须自行创建这个实例;
# 三是它必须自行向整个系统提供这个实例。
class _Singleton(object):
pass
Singleton = _Singleton()
del _Singleton
another = Singleton.__class__() # 没用,绕过
# __new__
#方法1,实现__new__方法
#并在将一个类的实例绑定到类变量_instance上,
#如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回
#如果cls._instance不为None,直接返回cls._instance
class Singleton(object):
_instance = None
def __new__(cls, *args, **kargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kargs)
return cls._instance
if __name__ == '__main__':
s1 = Singleton()
s2 = Singleton()
assert id(s1) == id(s2)
# 解决并发,引入带锁版
import threading
class Singleton(object):
objs = {}
objs_locker = threading.Lock()
def __new__(cls, *args, **kargs):
if cls in cls.objs:
return cls.objs[cls]
cls.objs_locker.acquire()
try:
if cls in cls.objs: ## double check locking
return cls.objs[cls]
cls.objs[cls] = object.__new__(cls)
finally:
cls.objs_locker.release()
# 利用经典的双检查锁机制,确保了在并发环境下Singleton的正确实现。
# 但这个方案并不完美,至少还有以下两个问题:
# ·如果Singleton的子类重载了__new__()方法,会覆盖或者干扰Singleton类中__new__()的执行,虽然这种情况出现的概率极小,但不可忽视。
# ·如果子类有__init__()方法,那么每次实例化该Singleton的时候,
# __init__()都会被调用到,这显然是不应该的,__init__()只应该在创建实例的时候被调用一次。
# 这两个问题当然可以解决,比如通过文档告知其他程序员,子类化Singleton的时候,请务必记得调用父类的__new__()方法;
# 而第二个问题也可以通过偷偷地替换掉__init__()方法来确保它只调用一次。
# 但是,为了实现一个单例,做大量的、水面之下的工作让人感觉相当不Pythonic。
# 这也引起了Python社区的反思,有人开始重新审视Python的语法元素,发现模块采用的其实是天然的单例的实现方式。
# ·所有的变量都会绑定到模块。
# ·模块只初始化一次。
# ·import机制是线程安全的(保证了在并发状态下模块也只有一个实例)。
# 当我们想要实现一个游戏世界时,只需简单地创建World.py就可以了。
# World.py
import Sun
def run():
while True:
Sun.rise()
Sun.set()
# main.py
import World
World.run()
工厂模式
两种方法
定义了一个创建对象的接口(可以理解为函数),但由子类决定要实例化的类是哪一个,工厂方法模式让类的实例化推迟到子类,抽象的CarStore提供了一个创建对象的方法createCar,也叫作工厂方法。
class Human(object):
def __init__(self):
self.name = None
self.gender = None
def getName(self):
return self.name
def getGender(self):
return self.gender
class Man(Human):
def __init__(self, name):
print(f'Hi,man {name}')
class Woman(Human):
def __init__(self, name):
print(f'Hi,woman {name}')
##################工厂模式1##################
class Factory:
def getPerson(self, name, gender):
if gender == 'M':
return Man(name)
elif gender == 'F':
return Woman(name)
else:
pass
if __name__ == '__main__':
factory = Factory()
person = factory.getPerson("Adam", "M")
# 返回在函数内动态创建的类###############
def factory2(func):
class klass: pass
#setattr需要三个参数:对象、key、value
setattr(klass, func.__name__, func)
#这里如果写classmethod(func)会变成类函数
return klass
def say_foo(self):
print('bar')
Foo = factory2(say_foo)
foo = Foo()
foo.say_foo()
元类
• 元类是关于类的类,是类的模板。
• 元类是用来控制如何创建类的,正如类是创建对象的模板一样。
• 元类的实例为类,正如类的实例为对象
• 创建元类的两种方法
- class
- type
• type(类名,父类的元组(根据继承的需要,可以为空,包含属性的字典(名字和值))
# 使用type元类创建类
def hi():
print('Hi metaclass')
# type的三个参数:类名、父类的元组、类的成员
Foo = type('Foo',(),{'say_hi':hi})#hi是对象,加了()是执行
foo = Foo
foo.say_hi()
# 元类type首先是一个类,所以比类工厂的方法更灵活多变,可以*创建子类来扩展元类的能力
def pop_value(self,dict_value):
for key in self.keys():
if self.__getitem__(key) == dict_value:
self.pop(key)
break
# 元类要求,必须继承自type
class DelValue(type):
# 元类要求,必须实现new方法
def __new__(cls,name,bases,attrs):
attrs['pop_value'] = pop_value##对象
return type.__new__(cls,name,bases,attrs)
#意思是虽然继承自dict但是元类是delvalue
class DelDictValue(dict,metaclass=DelValue):
# python2的用法,在python3不支持
# __metaclass__ = DelValue
pass
d = DelDictValue()
d['a']='A'
d['b']='B'
d['c']='C'
d.pop_value('C')###这个功能一般的字典没有
for k,v in d.items():
print(k,v)
抽象基类
• 抽象基类(abstract base class,ABC)用来确保派生类实现了基类中的特定方法。
• 使用抽象基类的好处:
• 避免继承错误,使类层次易于理解和维护。
• 无法实例化基类。
• 如果忘记在其中一个子类中实现接口方法,要尽早报错。
from abc import ABC
class MyABC(ABC):
pass
MyABC.register(tuple)
assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)
#也可以通过元类的方式
from abc import ABCMeta, abstractmethod
class Base(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
@abstractmethod
def bar(self):
pass
class Concrete(Base):
def foo(self):
pass
c = Concrete() # TypeError
Mixin 模式
在程序运行过程中,重定义类的继承,即动态继承。好处:
• 可以在不修改任何源代码的情况下,对已有类进行扩展
• 进行组件的划分
def mixin(Klass, MixinKlass):
Klass.__bases__ = (MixinKlass,) + Klass.__bases__
class Fclass(object):
def text(self):
print('in FatherClass')
class S1class(Fclass):
pass
class MixinClass(object):
def text(self):
return super().text()
# print('in MixinClass')
class S2class(S1class, MixinClass):
pass
print(f' test1 : S1class MRO : {S1class.mro()}')
s1 = S1class()
s1.text()
mixin(S1class, MixinClass)
print(f' test2 : S1class MRO : {S1class.mro()}')
s1 = S1class()
s1.text()
print(f' test3 : S2class MRO : {S2class.mro()}')
s2 = S2class()
s2.text()
test1 : S1class MRO : [<class '__main__.S1class'>, <class '__main__.Fclass'>, <class 'object'>]
in FatherClass
test2 : S1class MRO : [<class '__main__.S1class'>, <class '__main__.MixinClass'>, <class '__main__.Fclass'>, <class 'object'>]
in FatherClass
test3 : S2class MRO : [<class '__main__.S2class'>, <class '__main__.S1class'>, <class '__main__.MixinClass'>, <class '__main__.Fclass'>, <class 'object'>]
in FatherClass