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

问题描述 投票:12回答:2

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

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

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

目标:我可以导入MyLibPackage,它具有来自base_classes(= external project)的所有类。如果需要修改某些类或函数,可以在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.base_classes.shelf(get_books)时,MyLibPackage必须使用它。

python class
2个回答
19
投票

您想要做的被称为“猴子修补”,与“对象定向”无关。

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

也许使用像Zope Component Architecture这样的框架,该框架允许您标记带有接口的类,并提供适配器对象,以便您可以干净地使用一个对象,因为它具有最初设计时没有的某些接口。想法。

就是说,您要的是在其他模块中的相应位置更改该类-以便所有其他模块都可以看到更改。

您只是这样做:在所属模块中更改类。在Python中,只需在原始模块中将新类分配给所需的名称即可完成:

import base_classes

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

base_classes.Bookcollection = Bookcollection

(为了使这种工作正常进行,在任何大于单个脚本的项目中,都必须避免“ from x import *”-在这种情况下,您有两个名称相同且在整个代码中含义不同的变量:基类,以及继承的类,例如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。无论采用哪种方法,都可以编写单元测试。


1
投票
具有相同名称的类通常在您遇到名称空间冲突时是个坏主意。我建议将您的MyLibPackage.base_classes.Bookcollection更改为MyLibPackage.base_classes.BaseBookcollection或类似名称。然后,这应该会按预期工作。
© www.soinside.com 2019 - 2024. All rights reserved.