7.面向对象进阶

类属性和类对象

类属性字段在内存中只保存一份
对象属性每个对象都保存一份
类也是对象

作用域

• _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 类需要实现 getsetdelete 方法
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

单例模式

  1. 对象只存在一个实例
  2. __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()

元类

• 元类是关于类的类,是类的模板。
• 元类是用来控制如何创建类的,正如类是创建对象的模板一样。
• 元类的实例为类,正如类的实例为对象
• 创建元类的两种方法

  1. class
  2. 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
上一篇:2022 1-16


下一篇:c语言复习:走迷宫