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

2024-09-24 22:18:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我创建了一个python包/MyLibPackage,并将其导入到我的项目中。

MyLibPackage.____init____.py包括mymodiciation.py。此外,MyLibPackage文件夹还包含另一个文件:base_classes.py(=外部项目)

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

目标: 我可以导入MyLibPackage,它包含来自基类(=外部项目)的所有类。 如果我需要修改一些类或函数,我可以在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.mymodification.Bookcollection的重写类

我该怎么说:如果我在mymodification中重写了一个类,那么MyLibPackage必须使用它,尽管当调用从MyLibPackage.base_classes.shelf(get_books)访问时。


Tags: 项目frompyimportnewbasegetbooks
2条回答

当您遇到名称空间冲突时,使用相同名称的类通常是一个坏主意。我建议把你的MyLibPackage.base_classes.Bookcollection改成MyLibPackage.base_classes.BaseBookcollection或类似的。这样就可以按预期工作了。

你想做的是所谓的“猴子修补”,与对象的方向没有什么关系。

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

也许使用像Zope组件体系结构这样的框架会更好,它允许您用接口标记类,并提供适配器对象,这样您就可以干净地使用一个对象,因为它有一些最初设计时没有考虑到的接口。

也就是说,你所要求的是改变类,在另一个模块中,在它所在的位置-这样改变对所有其他模块都是可见的。

您可以这样做:更改模块中它所属的类。在Python中,只需在origin模块中将新类归属于所需的名称即可:

import base_classes

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

base_classes.Bookcollection = Bookcollection

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

因此,这将更改基本类模块中的Bookcollection类,但仅适用于从此点开始并在执行链上引用它的代码。如果示例中的“x”类是在“基本类”模块中定义的,或者是在“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组件体系结构。无论采用什么方法,都要编写单元测试。

相关问题 更多 >