OOP-描述符与设计模式

1.描述符

当一个类中,包含了三个魔术方法(__get__,__set__,__delete__)之一或者全部时,那么这个类就称为描述符类

1.1 作用

  • 描述符的作用就是对一个类中的某个成员进行一个详细的管理操作(获取,赋值,删除);
  • 描述符就是代理了一个类中的成员的操作,描述符属于类,只能定义类的属性
  • 一个类的成员的值是另一个描述符类的对象;那么当对这个类的成员进行操作时,等于是在操作另一个对象

1.2 三个魔术方法

'''
__get__(self, instance, owner)
	触发机制:在访问对象成员属性时自动触发(当该成员已经交给描述符管理时)
	作用:设置当前属性获取的值
	参数:1. self 描述符对象 2.被管理成员的类的对象。3.被管理成员的类
	返回值:返回值作为成员属性获取的值
	注意事项:无
__set__(self, instance, value)
	触发机制:在设置对象成员属性时自动触发(当该成员已经交给描述符管理时)
	作用:对成员的赋值进行管理
	参数:1. self 描述符对象 2.被管理成员的类的对象。3.要设置的值
	返回值:无
	注意事项:无
__delete__(self, instance)
	触发机制:在删除对象成员属性时自动触发(当该成员已经交给描述符管理时)
	作用:对成员属性的删除进行管理
	参数:1. self 描述符对象 2.被管理成员的类的对象。
	返回值:无
	注意事项:无
'''

1.3 数据描述符:(完整)

同时具备三个魔术方法的类就是数据描述符

##1.4 非数据描述符:(不完整)

没有同时具备三个魔术方法的类就是非描述符类

1.5 基本使用格式

把当前的描述符类赋值给一个需要代理的类中的成员属性

代码示例:

# 定义描述符类
class PersonName():
    __name = 'abc'

    def __get__(self, instance, owner):
        return self.__name

    def __set__(self, instance, value):
        self.__name = value

    def __delete__(self, instance):
        #del self.__name  如果没有这一行就没法删除
        print('不允许删除')

# 定义的普通类
class Person():
    # 把类中的一个成员属性交给一个描述符类来实现
    # 一个类中的成员的值是另一个描述符类的对象()
    # 那么当对这个类中得成员进行操作时,可以理解为就是对另一个对象的操作
    name = PersonName()

# 实例化对象
zs = Person()

1.6 描述符应用解析

#定义一个学生类,需要记录 学员的id,名字,分数
class Student():
    def __init__(self,id,name,score):
        self.id = id
        self.name = name
        self.score = score

    def returnMe(self):
        info =  f'''
        学员编号:{self.id}
        学员姓名:{self.name}
        学员分数:{self.score}
        '''
        print(info)
        
'''
# 要求:学员的分数只能在0-100范围中
解决方法:
    1.在__init__方法中检测当前分数范围
         # 检测分数范围
        if score >= 0 and score <= 100:
            self.score = score
       【但是这个解决方案只能在对象初始化时有效】
    2.定义一个setattr魔术方法检测
        检测如果给score分数进行赋值时,进行分数的检测判断
            def __setattr__(self, key, value):
        # 检测是否是给score进行赋值操作
        if key == 'score':
            # 检测分数范围
            if value >= 0 and value <= 100:
                object.__setattr__(self, key, value)
            else:
                print('当前分数不符合要求')
        else:
            object.__setattr__(self,key,value)

        【假如学员的分数不止一个时怎么办,比如 语文分数,数学分数,英语分数;
         而且会导致当前这个类中的代码多很多。】

    3.可以使用描述符来代理分数这个属性:
        1.定义Score描述符类
        2.把学生类中的score这个成员交给描述符类进行代理
        3.只要在代理的描述符类中对分数进行赋值和获取就ok了
'''

#定义描述符类 代理分数的管理
class Score():
    def __get__(self, instance, owner):
        return self.__score
    def __set__(self, instance, value):
        if value >= 0 and value <= 100:
            self.__score = value
        else:
            print('分数不符合要求')

# 使用描述符类代理score分数属性
class Student():
    score = Score()
    def __init__(self,id,name,score):
        self.id = id
        self.name = name
        self.score = score

    def returnMe(self):
        info =  f'''
        学员编号:{self.id}
        学员姓名:{self.name}
        学员分数:{self.score}
        '''
        print(info)

# 实例化对象
zs = Student(1011,'张三疯',99)
zs.returnMe()
zs.score = -20
zs.score = 88
zs.returnMe()

##1.7 描述符的三种定义格式

  • 格式一:通过定义描述符类来实现
class StudentScore():
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

class Student():
    score = StudentScore()
  • 格式二:使用 property 函数来实现
class Student():

    # 在当前需要被管理的类中 直接定义类似下面三个方法
    def getscore(self):
        print('getscore')

    def setscore(self,value):
        print('setscore',value)

    def delscore(self):
        print('delscore')

    # 在 property 函数中指定对应的三个方法,对应的方法 		1.__get__,2.__set__,3.__delete__
    score = property(getscore,setscore,delscore)

zs = Student()
  • 格式三:使用 @property 装饰器语法来实现
class Student():
    __score = None

    @property
    def score(self):
        print('get')
        return self.__score

    @score.setter
    def score(self,value):
        print('set')
        self.__score = value

    @score.deleter
    def score(self):
        print('delete')
        del self.__score

zs = Student()
print(zs.score)
zs.score = 199
print(zs.score)
del zs.score

#2.设计模式

设计模式是前人为完成某个功能或需求,根据经验和总结,对实现的代码步骤和代码设计进行了总结和归纳,成为了实现某个需求的经典模式。

设计模式并不是固定的代码格式,而是一种面向对象编程的设计

2.1 单例(单态)设计模式

在当前脚本中,同一个类只能创建出一个对象去使用。这种情况就成为单例(单态)。

'''
1.需要有一个方法,可以去控制当前对象的创建过程:
    构造方法 __new__
2.需要有一个标识来存储和表示是否有对象:
    创建一个私有属性进行存储,默认值为None
3.在创建对象的方法中去检测和判断是否有对象:
    如果没有对象,则创建对象,并且把对象存储起来,返回对象;
    如果存储的是对象,则直接返回对象,就不需要创建新的对象了
'''

示例:

class Demo():
    # 2.定义私有属性存储对象,默认值为None
    __obj = None

    # 1.定义构造方法
    def __new__(cls, *args, **kwargs):
        # 3.在创建对象的过程中,判断是否有对象
        if not cls.__obj:
            # 判断如果没有对象,则创建对象
            a = object.__new__(cls)
            # 并且存储起来
            cls.__obj = a
        # 直接把存储的对象返回
        return cls.__obj


# 实例化对象
a = Demo()
b = Demo()
print(a)
print(b)
>>><__main__.Demo object at 0x106f4d850>
   <__main__.Demo object at 0x106f4d850>
#注意!此时a和b就是同一个对象

2.2 Mixin混合设计模式

python 中的Mixin是通过多继承实现的。

java语言中可以通过 interface 接口类 来实现多重继承,
python中本身就支持 多继承关系。

Mixin类

  • Mixin 必须是表示一种功能,而不是一个对象【也就是不单独实例化一个对象】
  • Mixin 的功能必须单一,如果有多个功能,那就多定义Mixin类
  • python中的Mixin是通过多继承实现的;
  • Mixin 这个类通常不单独使用,而是混合到其它类中,去增加功能;
  • Mixin 类不干扰子类的实现,即便子类没有继承这个Mixin,子类也能正常运行,可能就是缺少了一些功能。

###使用Minxin混合类的好处

  • Mixin 这个混入类的设计模式,在不对类的内容修改的前提下,扩展了类的功能;
  • Mixin 混入类为了提高代码的重用性,使得代码结构更加简单清晰;
  • 可以根据开发需要任意调整功能(创建新的Mixin混入类),避免设计多层次的复杂的继承关系。(继承的关系最好不要超过两层,不然就会很混乱。)

示例:

'''
继承需要有一个必要的前提,继承应该是一个 'is-a' 的关系
例如:
    苹果可以去继承水果,因为苹果就是一个水果
    苹果不能继承午饭,因为午饭可以有苹果也可以没有

    比如 汽车可以继承 交通工具,因为汽车本身就是一个交通工具

交通工具有哪些?
汽车,飞机,直升机,这些都属于 交通工具
那么如何去设计这些类的关系呢?
比如创建一个交通工具类,然后属于交通工具的都来继承,再去实现。。。
但是,飞机和直升机都有飞行的功能,而汽车并没有,那么在交通工具中如果去定义 飞行这个功能,那就不太合适了。。
能不能在飞机和直升机类中分别实现 飞行 这个功能呢?可以,但是代码又无法重用。

怎么办?
单独去定义交通工具类,和 飞行器 这个两个父类,这样飞机和直升机就可以去继承这两个类.
'''

# 交通工具 vehicle
class vehicle():
    # 运输货物
    def huo(self):
        print('运输货物')
    # 搭载乘客
    def ren(self):
        print('搭载乘客')

# 飞行器
class FlyingMixin():
    def fly(self):
        print('可以起飞了。。。')

# 定义汽车类
class cart(vehicle):
    pass

# 定义飞机
class airplane(vehicle,FlyingMixin):
    pass

# 定义直升机
class helicopter(vehicle,FlyingMixin):
    pass

# 此时去定义一个飞行器的类 Flying,让需要飞行的交通工具,直接继承这个类。可以解决这个问题。
# 但是,1。出现类多继承,违背了'is-a'  2。飞行器这个类很容易被误解
# 解决方案也是使用多继承,但是给飞行器这个类,定义成为一个 Mixin 混合类,
# 此时就是等于把飞行器这个类,作为了一个扩展的功能,来扩展其它类

'''
在上面的代码中,虽然直升机和飞机都使用了多继承,也就是继承了FlyingMixin
但是由于 FlyingMixin 类加了 Minin这个名,就告诉了后面阅读代码的人,这个类是一个Mixin类
'''

#2.3 抽象类(了解)

抽象类是一个特殊的类:

  1. 含有抽象方法的抽象类不能直接实例化对象。【虽然不含抽象方法的抽象类可以直接实例化对象,但是一般都不这样搞,不然这个抽象类就没意义了】
  2. 抽象类中包含了抽象方法:抽象方法就是没有实现代码的方法。
  3. 抽象类需要子类继承,并重写父类的抽象方法,才可以使用。
  4. 定义抽象类时,需要继承一个metaclass类,也就是说:这个类的 metaclass属性必须是 metaclass=abc.ABCMeta。也就是类名后面的括号里写metaclass=abc.ABCMeta
  5. 在抽象类里定义抽象方法时,需要使用装饰器@abc.abstractmethod进行装饰。也就是在方法前面加一行代码:@abc.abstractmethod

​ 抽象类,一般应用在程序设计,程序设计中一般是要对功能和需求进行规划,其中有一些需求是明确的并且可以完成的,但是也可能会有一些需求是不明确的,或者不确定具体需要怎么实现。
那么此时就可以把这个不确定怎么实现或者需要后面再去实现的方法,定义为抽象方法(只定义方法名,不写具体代码)

'''
举例:
    公司有一项新的产品需要开发,交给了A,
    A规划设计怎么去完成这个产品的开发,
    必须项目需要用到不同的技术,需要不同的人来完成,
    然后A自己完成了一部分功能,还有一部分定义了需求,但是没有具体实现,需要其他人来实现

    这样,已经完成的部分就是 普通方法,定义了但是未完成的就可以是 抽象方法
'''

抽象类的应用:
例如要开发一个框架,这个框架要有一些基本功能和扩展功能;
但是使用者具体用这个框架开发什么样的产品,开发者并不清楚或者确定。
因此框架就具备一定的功能,并且留下来一些方法的定义,剩下的就是需要自己在方法中具体实现自己业务逻辑。

示例:

import abc #注意先导入模块

# 如果要定义为抽象类,需要继承一个metaclass类,也就是说:
# 这个类的 metaclass属性必须是 metaclass=abc.ABCMeta
class WriteCode(metaclass=abc.ABCMeta):

    #需要抽象的方法,使用装饰器进行装饰
    @abc.abstractmethod
    def write_php(self):
        pass

    def write_java(self):
        print('实现了java代码的开发')

    def write_python(self):
        print('实现了python代码的开发')


# 含有抽象方法的抽象类不能直接实例化对象
# obj = WriteCode()
# print(obj)
#TypeError: Can't instantiate abstract class WriteCode with abstract methods write_php


# 定义子类,继承抽象类,并实现抽象类中的抽象方法
class Demo(WriteCode):
    def write_php(self):
        print('实现了php代码的开发')

a = Demo()
print(a)
a.write_java()
a.write_php()
a.write_python()
>>><__main__.Demo object at 0x000001DCF25E7548>
   实现了java代码的开发
   实现了php代码的开发
   实现了python代码的开发

【注意!如果抽象类里没有抽象方法,那么这个时候就可以直接实例化对象。但是一般都不这样搞,不然这个抽象类就没意义了

import abc

# 如果要定义为抽象类,需要继承一个metaclass类,也就是说:
# 这个类的 metaclass属性必须是 metaclass=abc.ABCMeta
class WriteCode(metaclass=abc.ABCMeta):

    # #需要抽象的方法,使用装饰器进行装饰
    # @abc.abstractmethod
    # def write_php(self):
    #     pass

    def write_java(self):
        print('实现了java代码的开发')

    def write_python(self):
        print('实现了python代码的开发')


# 此时抽象类中没有抽象方法
obj = WriteCode()
print(obj)
>>><__main__.WriteCode object at 0x00000197B3A323C8>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值