Given a method, how do I return the class it belongs to in Python 3.3 onward?
在x = C.f后:
1 2 3
| class C:
def f(self):
pass |
怎么在一个呼叫xC回报?
最好的我可以做的是一份x.__qualname__execing parsed学院,这是丑陋的。
1
| exec('d = ' +".".join(x.__qualname__.split('.')[:-1])) |
一个用例,想象我想要的那一类super鱼鳞,调用任何方法,它的应用。这是如何装饰,只把给定的函数对象类,一个super(???下面)?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def ensure_finished(iterator):
try:
next(iterator)
except StopIteration:
return
else:
raise RuntimeError
def derived_generator(method):
def new_method(self, *args, **kwargs):
x = method(self, *args, **kwargs)
y = getattr(super(???, self), method.__name__)\
(*args, **kwargs)
for a, b in zip(x, y):
assert a is None and b is None
yield
ensure_finished(x)
ensure_finished(y)
return new_method |
- 埃多克斯1〔0〕怎么样?
- @达诺:我的ipython 3.4不管用吗?
- 定义方法的get类可能重复
- 啊,对不起,只有Python2。
- @尤尔:是的,但我已经更新了我的问题以指定python 3。我怀疑这是不可能的,因为python 3似乎已经取消了未绑定方法的types.methodType?
- 是否有需要此信息的特定用例?在python 3中,取消了未绑定方法的概念,因此x只是一个完全不与C直接相关的正则函数。
- @达诺:我最近的一个问题是:stackoverflow.com/questions/25921537/…
- 这个问题的答案是什么:stackoverflow.com/questions/961048/…。可能比你现有的更丑,但会在Python2上工作(但这不是你的要求)
- @它使用的x.im_class与python 3.x中没有的相同。
- 对不起,你是对的。意外地跑上了Python2.7
- 值得注意的是,你不应该使用im_class或self.__class__作为super的论点(根据他的另一个问题,这是操作的意图),因为如果你子类C,当调用f时,你会得到无限循环。
- 可能是这样的-x.__globals__['.'.join(x.__qualname__.split('.')[:-1])]?仍然很难看,但至少没有一个exec…
- 你有没有试过用x.__self__作为对象实例?或者,对于这个班,用x.__self__.__class__。
- @Ashwin:AttributeError: 'function' object has no attribute '__self__'。
- 你看过这个吗?stackoverflow.com/questions/3589311/…
- 我认为只要你这样做,你可能会有问题。因为您是在类本身(而不是类的实例)上操作,所以函数x将被分离。我看到的唯一方法是通过类似于x = C().f的实例来获取f上的函数。
- @迈克尔佩奇:我仍然感兴趣的是,是否需要qualname来解决这个问题,所以这不是一个关于python 3.3的问题,但是这很好,因为没有理由让任何人使用python 3.0–3.2。
- 它出现在3.0到3.2下,您可能没有任何选择。我建议的唯一一件事是,如果在3.0-3.2下分配了一个变量之后,您希望保留一个与它的类相关联的方法(并将其视为一个方法),那么您可能需要考虑通过一个实例(而不是类)获取一个方法。所以你可以使用x = C().f,而不是x = C.f。但是,如果这不适合你的需要,那么我现在不知道还有其他的解决方案。
- @Eryksun:不,C.f是一种方法:它是一个属于C类的函数,引用self。确实,python 3停止使用MethodType来处理未绑定的方法,但在语义上,它是一个方法。
- @Neilg,它在语义上是面向对象编程设计的一种方法,但在技术上它只是一个函数对象。python允许您在运行时向类中添加一个函数(方法),因此函数的__qualname__不一定与类相关。同样,像这样解析__qualname__也超出了它的预期用途。我发现了一个与关闭有关的问题;我不确定现在或将来的版本中可能会出现什么其他关键案例。
- @艾克森:是的。如果一个函数在运行时被添加到一个类中,那么一个经过修饰的函数版本如何调用super?我想这是不可能的。不幸的是,python没有用于模块、类和类成员的__parent__成员,因此可以遍历声明结构。
- @很遗憾,我在类中的方法上使用decorator,直到类定义完成后才定义类名。
- @尼尔,好吧,也许这能奏效。使用decorator在目标函数(如func._derived = True上)上设置标识属性。然后在类修饰器中定义derived_generator,该修饰器在类上创建一个闭包,因此可以调用super(cls, self)。循环类dict,将其应用于所有_derived函数。
- @艾克森:是的,好主意。它避免了我使用元类的解决方案,这是我想要避免的。你应该把这个评论抄到我另一个问题的答案里。
- 由于我如何在python 3中的方法修饰器中调用super与附加的用例是相同的问题,并且该问题的答案是"做这个问题中的事情",所以我将它作为dup关闭并复制了用例。@Neilg,如果你认为不合适的话,你可以回顾一下编辑和撤消。
- @谢谢。
如果您的目标是摆脱exec语句,但愿意使用__qualname__属性,即使您仍然需要手动解析它,那么至少在简单的情况下,以下似乎可以工作:
1
| x.__globals__[x.__qualname__.rsplit('.', 1)[0]] |
或:
1
| getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0]) |
号
我不是Python专家,但考虑到以下文档摘录,我认为第二个解决方案更好:
编辑:
正如@eryksun在评论中所指出的,像这样分析EDOCX1[1]超出了它的预期用途,考虑到EDOCX1[1]如何反映闭包,它非常脆弱。更健壮的方法需要排除形式为name.的闭包名称空间。例如:
1 2 3 4 5 6 7 8 9 10 11 12
| >>> class C:
... f = (lambda x: lambda s: x)(1)
...
>>> x = C.f
>>> x
<function C.<lambda>.<locals>.<lambda> at 0x7f13b58df730>
>>> x.__qualname__
'C.<lambda>.<locals>.<lambda>'
>>> getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0])
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'C.<lambda>.<locals>' |
具体情况可按以下方式处理:
1 2 3
| >>> getattr(inspect.getmodule(x),
... x.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
<class '__main__.C'> |
。
尽管如此,目前还不清楚在未来的版本中还会出现什么其他的角落案例。
正如@michaelpetch在评论中所指出的,这个答案只与Python 3.3之后的版本相关,因为只有在那时__qualname__属性才被引入到语言中。
- 然而,根据@wouterbolsterlee的说法,github.com/wbolster/qualname提供了与旧版Python版本相同的版本。
有关处理绑定方法的完整解决方案,请参阅此答案。
- 我担心的是目标是python 3。但直到python 3.4才添加__qualname__。那么,是否必须将问题修改为python 3.4及更高版本?此解决方案不适用于Python3.2和3.3变体。
- @迈克尔,我不知道。我已经编辑了我的答案以反映你的评论。谢谢!
- 我假设使用__qualname__是安全的,因为OP在他的问题中使用它作为可能的解决方案。我建议对问题进行标签编辑,以反映您的观察结果。
- 是的,他在问题体中使用过。而且,它似乎是在Python 3.3中引入的。
- @迈克尔佩奇:只是供你参考,不清楚或不好的问题是投反对票。如果有一个python 3解决方案是很好的,并且有一个答案说在python3.0-3.2中是不可能的,但是这就是在python3.3+中如何做到的,这是我问题的一个很好的答案。
- @迈克尔:不管你投反对票,我认为你应该考虑投反对票,因为你对这个问题非常关注。
- 这是一个糟糕的问题,因为标题和标签与接受的答案不匹配。如果被接受的答案与问题不匹配,那么问题就是坏的。
- @迈克尔:他所要做的就是说在3.0到3.2之间是不可能的。
- @尼尔,我想他不知道。正如他所说的那样,他放弃了这样一个事实:在你的评论中,你使用的是__qualname__,因此正如他所说的,他认为他可以使用它。我必须验证它在3.3版本中有效,所以我将取消对问题的投反对票,并将答案投反对票。
- github.com/wbolster/qualname为旧的python版本提供了等效的qualname。
- @Wouterbolsterlee:谢谢,我已经相应地编辑了我的答案。