我创建了一个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()
Run Code Online (Sandbox Code Playgroud)
如果我做:
from MyLibPackage import *
x = user()
books = x.get_books()
Run Code Online (Sandbox Code Playgroud)
然后对象Bookcollection具有属性"new_member".好!但如果我这样做:
from MyLibPackage import *
x = shelf() #this class is not overwritten and used also the object "Bookcolelction"
books = x.get_books()
Run Code Online (Sandbox Code Playgroud)
然后对象Bookcollection没有属性"new_member",因为他使用MyLibPackage.base_classes.Bookcollection实例化而不是我的覆盖类MyLibPackage.mymodiciation.Bookcollection
我怎么说:如果我在mymodiciation中覆盖一个类,那么MyLibPackage必须使用它,尽管当调用来自MyLibPackage.base_classes.shelf(get_books)时.
jsb*_*eno 19
你想做的事情叫做"猴子修补",与面向对象没什么关系.
Python确实支持它,但你可以控制你的所有类,你应该认真检查你的项目,检查你是否真的需要它.
也许使用像Zope组件体系结构这样的框架,它允许你用接口标记类,并提供适配器对象,这样你就可以干净地使用一个对象,因为它有一些设计不具备的接口,这将是一个更好的主意.
也就是说,您要求的是更改类,在另一个模块中,它所在的位置 - 以便所有其他模块都可以看到更改.
你这样做:更改它所属的模块中的类.在Python中,可以简单地将您的新类归属于原始模块中的所需名称:
import base_classes
class Bookcollection(base_classes.Bookcollection):
new_member = "lalala"
base_classes.Bookcollection = Bookcollection
Run Code Online (Sandbox Code Playgroud)
(强烈建议避免在大于单个脚本的任何项目中使用"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
Run Code Online (Sandbox Code Playgroud)
与尝试将新类(本身是一个对象)分配给原始模块中的相同名称相比,这将提供更一致的行为.
总而言之,您应该像@jamesj在他的回答中所做的那样,并使用不同的类,或者如果您需要动态行为,请使用可维护的框架,如Zope组件架构.而你采取任何方式,做写单元测试.
| 归档时间: |
|
| 查看次数: |
12193 次 |
| 最近记录: |