【问题标题】:Python: How to access outer class namespacesPython:如何访问外部类命名空间
【发布时间】:2016-11-02 18:14:05
【问题描述】:

考虑这段代码:

class A:
        B = 42  # or whatever
        class C:
                a = B  # wont find `B` here

这行不通,因为在class C 中找不到B。

您也不能使用A.B,因为此时A 是未知的,因为它仍在构建中,因此它不在全局命名空间中。在成员函数内部,这将起作用,因为在您执行该代码时,全局名称 A 可能已注册。但是,我明确要求直接访问,而不是在成员函数内部。

那么,在这样的嵌套类构造中,还有其他方法可以访问外部命名空间吗?

我想答案可能是否定的,或者至少不是以一种干净的方式。我在class C 中添加了print(locals()),使用Python 2,我得到{'__module__': '__main__'},使用Python 3,我得到{'__qualname__': 'A.C', '__module__': '__main__'}。所以无论是本地还是全局都不会给我访问父本地命名空间的权限。

关于不干净的方式:sys._getframe(1).f_locals["B"] 将起作用。那是 A 类在构建时的本地人,B 在那里。

那么,如果没有干净的方法来做到这一点,还有什么其他的解决方案?我可以直接在全局命名空间中声明所有类,但我不喜欢这样(对于我的特定用例)。我可以将它们移动到子模块中并将它们导入A。但这会使代码阅读起来更加复杂。还有其他解决方案吗?


所以,我想它总是一样的,除非我不展示一个真实世界的例子,否则人们不会相信这样的用例是如何发生的。所以,这里是(via):

class structs:
    # .... many more

    class _anonymous_label(ctypes.Structure):
        _fields_ = [
            ('lb_type', ctypes.c_int),
            ('lb_str', ctypes.POINTER(ctypes.c_byte))]

    class _anonymous_labellist(ctypes.Structure):
        _fields_ = [
            ('ll_nlabels', ctypes.c_int),
            ('ll_label', ctypes.POINTER(_anonymous_label))]  # error

    # .... many more

【问题讨论】:

  • 看看这个例子是多么微不足道:你有什么用例不是微不足道的?
  • 我其实有点好奇。这和闭包有什么区别?内部函数在哪里可以访问外部范围?我尝试使用元类并使用它的 _new_ 来设置 Ca =B,但即使这样也令人窒息 - 未定义全局名称“B”。我确实意识到类声明本身不是函数,但范围方面“看起来一样”。另外,Python 3(我在 2)没有更改嵌套范围查找吗?
  • class structs: ... - 你为什么要这样做?即使您希望其中的类位于它们自己的命名空间中,这也是模块系统的工作,而不是嵌套类。制作一个structs 模块。
  • @JLPeyret:这和方法一样,它不在类中查找变量。嵌套在类作用域内的作用域永远不会在类作用域中查找变量。
  • 啊,是的。知道了。需要 self 或类名。那就不那么奇怪了。

标签: python namespaces


【解决方案1】:
class Constants:
    a = 1

class A:
    class B(Constants):
        pass

    class C(Constants):
        pass

print(A.B.a, A.C.a)

【讨论】:

  • 它们不共享相同的基本常量。在我的示例中,属性名称相同只是偶然。
【解决方案2】:

在内部类声明完成之后,我会为外部类中的内部类执行类变量赋值。它稍微复杂一点,非常明显并且有效:

class A:
    class B:
        a = 1
    class C:
        pass
    C.a = B.a

print(A.B.a, A.C.a)

【讨论】:

  • 不幸的是,这对我的情况不起作用,因为需要完成类构造(因为它是ctypes.Structure)。此外,当成员属性在外部定义时,代码编辑器不会喜欢这样。
【解决方案3】:

你怎么看

var = 1
class A:
    class B:
        a = var
    class C:
        a = var

【讨论】:

  • 我需要引用B,而不是像var这样的通用常量全局。
【解决方案4】:

您始终可以使用不公开的临时对象。但是,根据您的重要示例的外观,这可能不合适。

# foo module
ABC_ATTRIBUTE = 42
class A:
  B = ABC_ATTRIBUTE
  class C:
    a = ABC_ATTRIBUTE
del ABC_ATTRIBUTE  # no longer accessible as foo.ABC_ATTRIBUTE

【讨论】:

  • 这就是我所说的“我可以直接在全局命名空间中声明所有类”的意思。
  • @Albert 我明白了,你添加的例子清楚地说明了你为什么不想要那个。我不需要class struct,但这不会改变你想要解决的问题。
  • 因为我不希望所有成员都在全局命名空间中。就像在你的例子中一样,最后我可以有一个del xy 的大列表,但这似乎有点丑陋的解决方案。
  • @Albert 是的,对于你的情况,del 是非常丑陋的,我同意。但是将class struct 仅用于命名空间-我会为此使用子模块,因为它实际上就是这样。更不用说这避免了没有__qualname__的python版本的问题。
猜你喜欢
  • 2011-10-11
  • 1970-01-01
  • 2010-11-22
  • 2015-01-08
  • 2018-04-13
  • 1970-01-01
  • 2018-09-16
  • 2013-05-17
  • 2023-04-02
相关资源
最近更新 更多