首页 > 解决方案 > 如果您不信任全局变量,那么当您在一个方法内部时,您如何判断我们当前在哪个类中?

问题描述

这是一个关于内省的一般原则的问题。当您在一个方法中时,我们如何才能知道我们当前在哪个类中?

我们想要如下内容:

class FigNewton:
    def baz(self):
        current_class = MAGIC()

MAGIC是什么?以下是不可接受的,因为我们在调用时全局变量不受信任的环境中工作。定义时的全局变量是可信的,但在方法调用时不是全局变量。

class FigNewton:
    def baz(self):
        current_class = FigNewton

为什么全局变量在调用时不受信任?因为有下面这样的恶作剧:

class FigNewton:
    def baz(self):
        current_class = FigNewton
        print("banana")
        print("current_class ==", current_class.__name__)

import itertools
import string
print = lambda *args, print=print:\
    print(
        sum(
            map(
                    lambda stryng:\
                        int(''.join(
                            itertools.takewhile(
                                lambda ch: ch in string.ascii_lowercase
                            ,
                                stryng
                            )
                       ), base=36)
                ,
                    map(str, args)
                )
            , )
        )

print("apple")

obj = FigNewton()
FigNewton = "apple"
obj.baz()

输出是:

17995730
683010982
27999997387

而不是预期的:

apple
banana
current_class == FigNewton

下面是更多演示问题的代码:

class K0:
    print=print
    type=type
    def foo(self, *args):
        self.print(40 * "--")
        self.print('args == ', args)
        self.print("K0 version of foo is executing.")
        self.print("Are all references to class K0 lost?")
        self.print("Well, global label `K0` is ", self.type(K0).__qualname__, K0)
        # K0.__getattribute__(self, "whatever") ## ERROR!!!
        tsqn = self.type(self).__qualname__
        self.print(
            "type(self) is ", tsqn,
            ". Is that K0? ", ("yes" if tsqn == "K0" else "no"),
            sep=""
        )
        self.print(40 * "--")

##########################################################
def test(seed_class):
    Ks = [seed_class]
    for idx in (1, 2, 3):
        K = type("K{}".format(idx), (Ks[-1],), dict())
        Ks.append(K)

    class K4(Ks[-1]):
        def foo(self):
            print("K10 version of foo is executing")
            print("type(self) is ", type(self))

    # Begin messing up global namespace
    global K0
    K0 = 0
    # End messing up global namespace
    Ks.pop(0)
    for K in Ks:
        obj = K()
        obj.foo(1, 2, 3)
    return None
##########################################################
test(K0)

输出是:

--------------------------------------------------------------------------------
args ==  (1, 2, 3)
K0 version of foo is executing.
Are all references to class K0 lost?
Well, global label `K0` is  int 0
type(self) is K1. Is that K0? no
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
args ==  (1, 2, 3)
K0 version of foo is executing.
Are all references to class K0 lost?
Well, global label `K0` is  int 0
type(self) is K2. Is that K0? no
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
args ==  (1, 2, 3)
K0 version of foo is executing.
Are all references to class K0 lost?
Well, global label `K0` is  int 0
type(self) is K3. Is that K0? no
--------------------------------------------------------------------------------

标签: pythonpython-3.xintrospection

解决方案


在 CPython 引用解释器中,您可以通过简单地引用来获取定义方法的类__class__

class FigNewton:
    def baz(self):
        current_class = __class__  # Assigns FigNewton, even if instance of subclass is used to invoke method

据我所知,这是 CPython 的一个实现细节(它用于支持无参数super()调用,我认为除了在更改日志和 What's New 文档中传递之外没有提到它),所以不要依赖它在其他口译员上。

如果您想要运行时类型(因此即使在父类中定义的方法中调用它也会报告子类类型),请使用type(self)(或等效地,self.__class__):

class FigNewton:
    def baz(self):
        current_class = type(self)  # Assigns FigNewton or a subclass thereof if instance of subclass is used to invoke method

推荐阅读