千家信息网

python的单一职责原则怎么实现

发表于:2024-11-23 作者:千家信息网编辑
千家信息网最后更新 2024年11月23日,今天小编给大家分享一下python的单一职责原则怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们
千家信息网最后更新 2024年11月23日python的单一职责原则怎么实现

今天小编给大家分享一下python的单一职责原则怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

    一,封装

    封装是面向对象编程思想的重要特征之一。

    (一)什么是封装

    封装是一个抽象对象的过程,它容纳了对象的属性和行为实现细节,并以此对外提供公共访问。

    这样做有几个好处:

    • 分离使用与实现。可直接使用公共接口,但不需要考虑它内部具体怎么实现。

    • 拥有内部状态隐藏机制,可实现信息/状态隐藏。

    (二)封装与访问

    就面向对象编程来说,类就是实现对象抽象的手段,封装的实现,就是将对象的属性与行为抽象为类中属性与方法。

    举个例子:

    对象 AudioFile ,需要有文件名,还需要能播放与停止播放。用类封装的话,就类似于下面这个实现:

    class AudioFil:    def __init__(self, filename):        self.filename = filename    def play(self):        print("playing...")    def stop(self):        print("stop playing...")

    self参数必须是传入类方法的第一个(最左侧)参数;Python 会通过这个参数自动填入实例对象(也就是调用这个方法的主体)。这个参数不必叫self,其位置才是重点(C++或Java程序员可能更喜欢把它称作this,因为在这些语言中,该名称反应的是相同的概念。在Python中,这个参数总是需要明确的)。

    封装之后,能轻松实现访问:

    if __name__ == "__main__":    file_name = "金刚葫芦娃.mp3"    current_file = AudioFil(filename=file_name)    print(current_file.filename)    current_file.play()    current_file.stop()>>>金刚葫芦娃.mp3playing 金刚葫芦娃.mp3...stop playing 金刚葫芦娃.mp3...

    同时能在外部修改内部的属性:

    if __name__ == "__main__":    file_name = "金刚葫芦娃.mp3"    current_file = AudioFil(filename=file_name)    print(current_file.filename)    current_file.play()    current_file.stop()    current_file.filename = "舒克与贝塔.ogg"    print(current_file.filename)    current_file.play()    current_file.stop()>>>金刚葫芦娃.mp3playing 金刚葫芦娃.mp3...stop playing 金刚葫芦娃.mp3...舒克与贝塔.oggplaying 舒克与贝塔.ogg...stop playing 舒克与贝塔.ogg...

    (三)私有化与访问控制

    尽管能通过外部修改内部的属性或状态,但有时出于安全考虑,需要限制外部对内部某些属性或者方法的访问。

    一些语言能显式地指定内部属性或方法的有效访问范围。比如在 Java 中明确地有 publicprivate 等关键字提供对内部属性与方法的访问限制,但 python 并提供另一种方式将它们的访问范围控制在类的内部:

    • ___来修饰属性与方法,使之成为内部属性或方法。

    • __method-name__ 来实现方法重载。

    1,属性与方法的私有化

    举个例子:

    class AudioFil:    def __init__(self, filename):        self._filename = filename    def play(self):        print(f"playing {self._filename}...")    def stop(self):        print(f"stop playing {self._filename}...")if __name__ == "__main__":    file_name = "金刚葫芦娃.mp3"    current_file = AudioFil(filename=file_name)    print(current_file._filename)    current_file.play()    current_file.stop()

    注意 _filename 的格式,单下划线开头表明这是一个类的内部变量,它提醒程序员不要在外部随意访问这个变量,尽管是能够访问的。

    更加严格的形式是使用双下划线:

    class AudioFil:    def __init__(self, filename):        self.__filename = filename    def play(self):        print(f"playing {self.__filename}...")    def stop(self):        print(f"stop playing {self.__filename}...")if __name__ == "__main__":    file_name = "金刚葫芦娃.mp3"    current_file = AudioFil(filename=file_name)    print(current_file.__filename)  #AttributeError: 'AudioFil' object has no attribute '__filename'    current_file.play()    current_file.stop()

    注意 __filename 的格式,双下划线开头表明这是一个类的内部变量,它会给出更加严格的外部访问限制,但还是能够通过特殊手段实现外部访问:

        # print(current_file.__filename)    print(current_file._AudioFil__filename)

    _ClassName__attributename

    总之,这种私有化的手段"防君子不防小人",更何况这并非是真的私有化——伪私有化。有一个更加准确的概念来描述这种机制:变量名压缩。

    2,变量名压缩

    Python 支持变量名压缩(mangling,起到扩展作用)的概念——让类内某些变量局部化。

    压缩后的变量名通常会被误认为是私有属性,但这其实只是一种把类所创建的变量名局部化的方式而已:名称压缩并无法阻止类外代码对它的读取。

    这种机制主要是为了避免实例内的命名空间的冲突,而不是限制变量名的访问。因此,压缩过的变量名最好称为"伪私有",而不是"私有"。

    类内部以 ___ 开头进行命名的操作只是一个非正式的惯例,目的是让程序员知道这是一个不应该修改的名字(它对Python自身来说没有什么意义)。

    3,方法重载

    python 内置的数据类型自动地支持有些运算操作,比如 + 运算、索引、切片等,它们都是通过对应对象的类的内部的以 __method-name__ 格式命名的方法来实现的。

    方法重载可用于实现模拟内置类型的对象(例如,序列或像矩阵这样的数值对象),以及模拟代码中所预期的内置类型接口。

    最常用的重载方法是__init__构造方法,几乎每个类都使用这个方法为实例属性进行初始化或执行其他的启动任务。

    方法中特殊的self参数和__init__构造方法是 Python OOP的两个基石

    举个例子:

    class AudioFil:    def __init__(self, filename):        self.__filename = filename    def __str__(self):        return f"我是《{self.__filename}》"    def play(self):        print(f"playing {self.__filename}...")    def stop(self):        print(f"stop playing {self.__filename}...")if __name__ == "__main__":    file_name = "金刚葫芦娃.mp3"    current_file = AudioFil(filename=file_name)    print(current_file)             #>>> 我是《金刚葫芦娃.mp3》

    (四)属性引用:getter、setter 与 property

    一些语言使用私有属性的方式是通过 getter 与 setter 来实现内部属性的获取与设置。python 提供 property 类来达到同样的目的。举个例子:

    class C:    def __init__(self):        self._x = None    def getx(self) -> str:        return self._x    def setx(self, value):        self._x = value    def delx(self):        del self._x    x = property(getx, setx, delx, "I'm the 'x' property.")if __name__ == '__main__':    c = C()    c.x = "ccc" # 调用setx    print(c.x)  # 调用getx    del c.x     # 调用delx

    property的存在让对属性的获取、设置、删除操作自动内置化。

    更加优雅的方式是使用@property装饰器。举个例子:

    class C:    def __init__(self):        self._x = None    @property    def x(self):        """I'm the 'x' property."""        return self._x    @x.setter    def x(self, value):        self._x = value    @x.deleter    def x(self):        del self._xif __name__ == '__main__':    c = C()    c.x = "ccc"    print(c.x)      del c.x

    二,单一职责原则

    (一)一个不满足单一职责原则的例子

    现在需要处理一些音频文件,除了一些描述性的属性之外,还拥有播放、停止播放和信息存储这三项行为:

    class AudioFile:    def __init__(self, filename, author):        self.__filename = filename        self.__author = author        self.__type = self.__filename.split(".")[-1]    def __str__(self):        return f"我是《{self.__filename}》"    def play(self):        print(f"playing {self.__filename}...")    def stop(self):        print(f"stop playing {self.__filename}...")    def save(self, filename):        content = {}        for item in self.__dict__:            key = item.split("__")[-1]            value = self.__dict__[item]            content[key] = value        with open(filename+".txt", "a") as file:            file.writelines(str(content)+'\n')if __name__ == '__main__':    file_name = "金刚葫芦娃.mp3"    author_name = "姚礼忠、吴应炬"    current_file = AudioFile(filename=file_name,author=author_name)    current_file.save(filename="info_list")

    这个类能够正常工作。

    注意观察 save 方法,在保存文件信息之前,它做了一些格式化的工作。显然后面的工作是"临时添加"的且在别的文件类型中可能也会用到。
    随着项目需求的变更或者其他原因,经常会在方法内部出现这种处理逻辑的扩散现象,即完成一个功能,需要新的功能作为前提保障。

    从最简单的代码可重用性的角度来说,应该将方法内可重用的工作单独提出来:

    至于公共功能放在哪个层次,请具体分析。

    def info_format(obj):    content = {}    for item in obj.__dict__:        key = item.split("__")[-1]        value = obj.__dict__[item]        content[key] = value    return contentclass AudioFile:    ...    def save(self, filename):        content = info_format(self)        with open(filename+".txt", "a") as file:            file.writelines(str(content)+'\n')

    但是,给改进后的代码在遇到功能变更时,任然需要花费大力气在原有基础上进行修改。比如需要提供信息搜索功能,就可能出现这种代码:

    class AudioFile:    ...    def save(self, filename):        ...    def search(self, filename, key=None):        ...

    如果后期搜索条件发生变更、或者再新增功能,都会导致类内部出现功能扩散,将进一步增加原有代码的复杂性,可读性逐渐变差,尤其不利于维护与测试。

    (二)单一职责原则

    单一职责原则(Single-Responsibility Principle,SRP)由罗伯特·C.马丁于《敏捷软件开发:原则、模式和实践》一书中提出。这里的职责是指类发生变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。

    该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:

    • 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;

    • 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

    举个例子:一个编译和打印报告的模块。想象这样一个模块可以出于两个原因进行更改。

    首先,报告的内容可能会发生变化。其次,报告的格式可能会发生变化。这两件事因不同的原因而变化。单一职责原则说问题的这两个方面实际上是两个独立的职责,因此应该在不同的类或模块中。

    总之,单一职责原则认为将在不同时间因不同原因而改变的两件事情结合起来是一个糟糕的设计。

    看一下修改后的代码:

    class AudioFile:    def __init__(self, filename, author):        self.__filename = filename        self.__author = author        self.__type = self.__filename.split(".")[-1]    def __str__(self):        return f"我是《{self.__filename}》"    def play(self):        print(f"playing {self.__filename}...")    def stop(self):        print(f"stop playing {self.__filename}...")class AudioFileDataPersistence:    def save(self, obj, filename):        ...class AudioFileDataSearch:    def search(self, key, filename):        ...if __name__ == '__main__':    file_name = "金刚葫芦娃.mp3"    author_name = "姚礼忠、吴应炬"    current_file = AudioFile(filename=file_name, author=author_name)    data_persistence = AudioFileDataPersistence()    data_persistence.save(current_file, filename="info_list")    data_search = AudioFileDataSearch()    data_search.search(file_name, filename="info_list")

    但这样将拆分代码,是不是合理的选择?

    三,封装与单一职责原则

    从封装的角度看来说,它的目的就是在对外提供接口的同时,提高代码的内聚性和可重用性,但功能大而全的封装更加的不安全。

    单一职责原则通过拆分代码实现更低的耦合性和更高的可重用性,但过度拆分会增加对象间交互的复杂性。

    关于两这的结合,有一些问题需要事先注意:

    • 需求的粒度是多大?

    • 维护的成本有多高?

    作为面向对象编程的基础概念与实践原则,二者实际上是因果关系——如果一个类是有凝聚力的,如果有一个更高层次的目的,如果它的职责符合它的名字,那么 SRP 就会自然而然地出现。SRP 只是代码优化后的实际的结果,它本身并不是一个目标。

    以上就是"python的单一职责原则怎么实现"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

    方法 职责 属性 对象 原则 葫芦 葫芦娃 金刚 代码 变量 封装 私有 功能 例子 原因 参数 c.x 变化 不同 两个 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 sql数据库变成单户 香港做软件开发工资高吗 帝国神话自定义服务器没有权限 为什么选择软件开发答案 网络技术服务财务会计核算 虹口区媒体数据库服务报价行情 胶州游戏软件开发哪家靠谱 电脑可以储存多少数据库 供电班组信息通信网络安全 惠普服务器u盘启动 cs韩国服务器正在维护 小程序需要什么样服务器 银行网络安全课程设计 大专计算机网络技术课件 奕云网络技术有限公司 河南航天金穗软件开发加班 安徽流耀网络技术有限公司 数据库系统原理教程 梦幻西游平转服务器在哪里 影音服务器显卡 怀旧服大型战场哪个服务器好 电脑数据库管理系统怎么查 图库关闭网络安全吗 数据库为什么要管理 互联网科技实验室 软件开发界面布局 抖音代运营是网络技术服务吗 华为网络技术工程师好进吗 问道每个区都是同一个服务器吗 HCNA网络技术目录
    0