我有一个名为iResource的接口类和许多子类,每个子类都实现了“request”方法。请求函数使用套接字I / O到其他机器,因此以异步方式运行它们是有意义的,因此其他机器可以并行工作。
问题是当我用iResource.request启动一个线程并给它一个子类作为第一个参数时,它将调用超类方法。如果我尝试用“type(a).request”和“a”作为第一个参数来启动它,那么我得到“”表示类型(a)的值。任何想法意味着什么以及如何获得方法的真正类型?我可以用某种方式在Python中正式声明一个抽象方法吗?
编辑:包含代码。
def getSocialResults(self, query=''):
#for a in self.types["social"]: print type(a)
tasks = [type(a).request for a in self.types["social"]]
argss = [(a, query, 0) for a in self.types["social"]]
grabbers = executeChainResults(tasks, argss)
return igrabber.cycleGrabber(grabbers)
“executeChainResults”获取callables的列表“tasks”和args-tuples的列表“argss”,并假设每个返回一个列表。然后它在一个单独的线程中执行每个,并连接结果列表。如果有必要,我可以发布该代码,但我没有遇到任何问题,所以我暂时将其删除。
对象“a”绝对不是iResource类型,因为它有一个只抛出异常的构造函数。但是,将“type(a).request”替换为“iResource.request”会调用基类方法。此外,调用“self.types [”social“] [0] .request”直接工作正常,但上面的代码告诉我:“类型对象'实例'没有属性'请求'”。
取消注释注释行多次打印<type 'instance'>
。
答案 0 :(得分:2)
您可以使用绑定方法对象本身:
tasks = [a.request for a in self.types["social"]]
# ^^^^^^^^^
grabbers = executeChainResults(tasks, [(query, 0)] * len(tasks))
# ^^^^^^^^^^^^^^^^^^^^^^^^^
如果你坚持通过基类调用你的方法,你也可以这样做:
from abc import ABCMeta
from functools import wraps
def virtualmethod(method):
method.__isabstractmethod__ = True
@wraps(method)
def wrapper(self, *args, **kwargs):
return getattr(self, method.__name__)(*args, **kwargs)
return wrapper
class IBase(object):
__metaclass__ = ABCMeta
@virtualmethod
def my_method(self, x, y):
pass
class AddImpl(IBase):
def my_method(self, x, y):
return x + y
class MulImpl(IBase):
def my_method(self, x, y):
return x * y
items = [AddImpl(), MulImpl()]
for each in items:
print IBase.my_method(each, 3, 4)
b = IBase() # <-- crash
结果:
7
12
Traceback (most recent call last):
File "testvirtual.py", line 30, in <module>
b = IBase()
TypeError: Can't instantiate abstract class IBase with abstract methods my_method
Python不支持接口,例如Java确实如此。但是使用abc
模块,您可以确保必须在子类中实现某些方法。通常你会使用abc.abstractmethod()
装饰器执行此操作,但是你仍然无法通过基类调用子类方法,就像你想要的那样。我有一个类似的问题,我有virtualmethod()
装饰的想法。这很简单。它基本上与abc.abstratmethod()
做同样的事情,但也将调用重定向到子类方法。可以在the docs和PEP3119中找到abc
模块的详细信息。
BTW:我假设您使用的是Python&gt; = 2.6。
答案 1 :(得分:1)
在Python中使用“旧样式类”时引用的“<type "instance" >
”引用 - 即:不是从“对象”类型层次结构派生的类。旧样式类不应该与几种语言的新功能一起使用,包括描述符和其他。 AND,除其他外, - 您无法使用您正在执行的操作从旧样式类的类中检索属性(或方法):
>>> class C(object):
... def c(self): pass
...
>>> type (c)
<class '__main__.C'>
>>> c = C()
>>> type(c).c
<unbound method C.c>
>>> class D: #not inheriting from object: old style class
... def d(self): pass
...
>>> d = D()
>>> type(d).d
>>> type(d)
<type 'instance'>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'instance' has no attribute 'd'
>>>
因此,只需使您的基类继承自“object”而不是“nothing”,并检查从类型(a)请求“request”方法时是否仍然收到错误消息:
至于你的其他观察: “问题是当我用iResource.request启动一个线程并给它一个子类作为第一个参数时,它将调用超类方法。”
它似乎正确的“正确”事情就是:
>>> class A(object):
... def b(self):
... print "super"
...
>>> class B(A):
... def b(self):
... print "child"
...
>>> b = B()
>>> A.b(b)
super
>>>
在这里,我在类“A”中调用一个方法,给它一个专门的“A”实例 - 该方法仍然是类“A”中的方法。