我正在GTK中编写应用程序。我们需要支持VTE终端API 2.90和2.91。所以我在名为'兼容性的模块中创建了两者之间的愚蠢兼容层。看起来像这样。
import gi
try:
gi.require_version('Vte', '2.91')
vte_version = '2.91'
except ValueError:
gi.require_version('Vte', '2.90')
vte_version = '2.90'
from gi.repository import Vte
class CompatibleVteTerminal(Vte.Terminal):
"""A simple VTE terminal modified to be compatible with both 2.90
and 2.91 API"""
def __init__(self):
Vte.Terminal.__init__(self)
def spawn_sync(self, pty_flags, working_directory, argument_vector,
env_variables, glib_spawn_flags, child_setup,
child_setup_data, cancellable=None):
"""Returns the corresponden version os 'spawn_sync' method
according to the Vte version the user has"""
if vte_version == '2.91':
return Vte.Terminal.spawn_sync(self, pty_flags, working_directory,
argument_vector, env_variables,
glib_spawn_flags, child_setup,
child_setup_data, cancellable)
elif vte_version == '2.90':
return Vte.Terminal.fork_command_full(self, pty_flags,
working_directory,
argument_vector, env_variables,
glib_spawn_flags, child_setup,
child_setup_data, cancellable)
现在,我已经在另一个模块中创建了一个终端。
class Terminal(Vte.Terminal):
"""Do terminal stuff"""
当然,我希望这个类现在从我的兼容终端继承。
from compatibility import CompatibleVteTerminal as Vte.Terminal
class Terminal(Vte.Terminal):
"""Do terminal stuff"""
但是这给了我一个无效的语法'错误。显然你不能在名字中导入带点的东西。如何在不修改原始代码的情况下导入兼容终端?
答案 0 :(得分:1)
我真的认为有更好的方法,这就是为什么我最初发布的评论而不是答案,但为了说明如何创建动态模块并为其添加类,请考虑以下内容:
class AlternativeCounter:
def __init__(self, *args):
pass
def __repr__(self):
return "HA! I'm an imposter!"
use_stdlib_collections = False
if use_stdlib_collections:
import collections
else:
import imp
collections = imp.new_module('collections')
collections.Counter = AlternativeCounter
class MyCounter(collections.Counter):
def foo(self):
print("I'm a %r" % self.__class__.__name__)
print("My parents are: %r" % self.__class__.__bases__)
c = MyCounter("12321")
c.foo()
print(c)
如果use_stdlib_collections = True
,则输出:
I'm a 'MyCounter' My parents are: <class 'collections.Counter'> MyCounter({'2': 2, '1': 2, '3': 1})
如果use_stdlib_collections = False
,则输出:
I'm a 'MyCounter' My parents are: <class '__main__.AlternativeCounter'> HA! I'm an imposter!
代码的顺序可能看起来很奇怪,为了将它全部保存在一个文件中,我必须在条件导入机制之上定义AlternativeCounter。在实践中,代码可能看起来更像:
use_stdlib_collections = False
if use_stdlib_collections:
import collections
else:
import imp
collections = imp.new_module('collections')
from some_other_module import AlternativeCounter
collections.Counter = AlternativeCounter
# ...
和AlternativeCounter
将放置在外部some_other_module
模块中。
根据您的使用情况,您甚至可能不需要创建实际模块,您可以使用空类:
# ...
class FakeModule: pass
if use_stdlib_collections:
import collections
else:
collections = FakeModule()
collections.Counter = AlternativeCounter
#...
请注意,在任何一种情况下,任何附加引用collections
模块(例如collections.OrderedDict
)(或者在您的情况下,对{{1)的其他引用没有额外的工作就会失败。
由于您想要访问模块的其他属性(在您的案例Vte
中,在我的Vte
中),您将需要一种在所有情况下“退回”真实模块的方法除了你明确覆盖的那些。
使用伪装成模块的类的方法,您可以执行以下操作:
collections
这里,class AlternativeCounter:
def __init__(self, *args):
pass
def __repr__(self):
return "HA! I'm an imposter!"
class ModuleWithFallback:
def __init__(self, backup):
self.backup = backup
# Provide a fallback mechanism for un-overridden attribute access
def __getattr__(self, name):
return getattr(self.backup, name)
use_stdlib_collections = False
if use_stdlib_collections:
import collections
else:
import collections as std_collections
collections = ModuleWithFallback(std_collections)
collections.Counter = AlternativeCounter
class MyCounter(collections.Counter):
def foo(self):
print("I'm a %r" % self.__class__.__name__)
print("My parents are: %r" % self.__class__.__bases__)
c = MyCounter("12321")
c.foo()
print(c)
print("---")
# Despite defaultdict not being overridden, it still works as you would expect.
dd = collections.defaultdict(int)
dd["bar"] += 1
print(dd["bar"]) # 1
print(type(dd)) # <class 'collections.defaultdict'>
是一个简单的类似代理的类。它的构造函数只接受一个参数,一个模块的参数,我在别名ModuleWithFallback
下导入,以免冲突。现在,当访问一个未在类上定义的属性时(在上面的示例中我使用std_collections
),它会尝试返回“backup”模块的相应属性(在我的例子中,真正的收集模块)。
请注意,此仍然可能不起作用,具体取决于您的课程的具体情况,但使用此代码至少您有机会。