python - 如何为所有调用覆盖导入的 python 类

标签 python class

我创建了一个 python-packages/MyLibPackage,我将在我的项目中导入它。

MyLibPackage.____init____.py 包括 mymodiciation.py。此外,MyLibPackage 文件夹包含另一个文件:base_classes.py(=external project)

mymodiciation.py 导入“from base_classes import *”。

目标: 我可以导入 MyLibPackage,它包含来自 base_classes(=外部项目)的所有类。 如果我需要修改一些类或函数,我可以在 mymodiciation.py 中覆盖它。它有效,但我遇到了问题。例如:

我在 mymodiciation.py 中覆盖了这个类:

class Bookcollection(Bookcollection):
   new_member = "lalala"


class user(user):
   def get_books(self):
      return Bookcollection()

如果我这样做:

from MyLibPackage import *
x = user()
books = x.get_books()

然后对象 Bookcollection 具有属性“new_member”。好的! 但如果我这样做:

from MyLibPackage import *
x = shelf() #this class is not overwritten and used also the object "Bookcolelction"
books = x.get_books()

然后对象 Bookcollection 没有属性“new_member”,因为他是用 MyLibPackage.base_classes.Bookcollection 实例化的,而不是用我覆盖的类 MyLibPackage.mymodiciation.Bookcollection 实例化的

我怎么说:如果我在 mymodiciation 中覆盖一个类,那么 MyLibPackage 必须使用它,尽管调用来自 MyLibPackage.base_classes.shelf (get_books)。

最佳答案

你想做的是所谓的“猴子补丁”,与面向对象无关。

Python 确实支持它,但您可以控制所有类,您应该认真检查您的项目以检查您是否真的需要它。

也许使用像 Zope Component Architecture 这样的框架,它允许你用接口(interface)标记类,并提供适配器对象,这样你就可以干净地使用一个对象,因为它有一些最初设计时没有设计的接口(interface),这将是一个更好的主意。

就是说,您要求的是更改类,在另一个模块中,它所在的位置 - 以便所有其他模块都可以看到更改。

您只需这样做:更改其所属模块中的类。在 Python 中,只需在原始模块中将新类赋予所需的名称即可:

import base_classes

class Bookcollection(base_classes.Bookcollection):
   new_member = "lalala"

base_classes.Bookcollection = Bookcollection

(为了使这样的事情起作用,您必须避免在任何大于单个脚本的项目中使用“from x import *”——在这种情况下,您有 2 个变量具有相同的名称,并且在整个代码中具有不同的含义:基础类,以及继承的类,例如。Python 命名空间允许您避免这种情况)。

因此,这将更改 base_class 模块中的 Bookcollection 类 - 但仅适用于将从此时开始以及在您的执行链上引用它的代码。如果示例中的“x”类在“base_classes”模块中定义,或者在导入“MyModule”之前定义,它将获得对旧“Bookcollection”类的引用。

如您所见,它很快就会变得一团糟,如果您真的选择了这种方法,那么保持项目可用的唯一方法就是进行单元测试以验证您想要修补的所有类都是实际上打了补丁。如您所见,即使是模块的导入顺序也会有所不同。如果您有测试地点,如果您按照破坏您的猴子补丁的顺序进行导入,它们就会中断。

如果您只需要在现有类中添加和替换东西,您可以猴子修补类本身来替换它的组件,而不是猴子修补它所在的模块来替换类。这样,模块的导入顺序就无关紧要了——它甚至会影响该类的现有实例:

 import base_classes

 base_classes.Bookcollection.new_member = "lalala"

 def new_bookcol_method(self):
      pass

 # to replace or register a method in the other class:
 base_classes.Bookcollection.__dict__["old_bookcol_method"] = new_bookcol_method

与尝试将新类(它本身是一个对象)分配给原始模块中的相同名称相比,这会给您带来更一致的行为。

总而言之,您应该按照@jamesj 在他的回答中建议的那样做,并使用不同的类,或者如果您需要动态行为,请为此使用可维护的框架,例如 Zope Component Architecture。无论您采用什么方法,编写单元测试。

关于python - 如何为所有调用覆盖导入的 python 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10027232/

相关文章:

python - 以毫秒为单位将 python 日期时间转换为时间戳

python - 如何用循环创建变量?

node.js - 如何在 Protractor 中设置 webElement 的属性?

C++ 类数组初始化

python - 在 Centos 上安装 mysql-python

Python 字典 : Creating a prepared statement

python - 将数据从 PDF 格式转换为 CSV

python zeep,如何更容易地找到方法/属性所属的绑定(bind)?

c++ - 在 C++ 中将列表作为返回类型传递?

c# - 通用静态类有多个实例?