【问题标题】:Two classes with the name in the same module with different bases?同一模块中名称不同的两个类?
【发布时间】:2014-04-21 08:46:04
【问题描述】:

我正在尝试设计一个类结构,其中类的基础在运行时确定。非常感谢the answer to this question about metaclasses,它点击type 函数可以通过在运行时构造基元组来完成此操作。太棒了。

现在这开启了我不确定自己是否满意的可能性——通过一些操作序列,我可以得到相当于:

X  = type('X', (object,), {})
Y  = type('Y', (X,), {})
Y2 = type('Y', (object,), {})

我现在有两个类(YY2)具有不同的继承结构,但都共享类名 'Y'

c  =  y.__class__
c2 = y2.__class__

(c,c2)                      #returns  (__main__.Y, __main__.Y)
(c.__bases__,c2.__bases__)  #returns  ((__main__.X,), (object,))

由此看来,类名只是供人类消费的任意字符串,而Python认为,如果我想去迷惑自己,我不客气。

正确吗?如果允许不同的类使用相同的名称,在 Python 的其他地方是否还有陷阱在等着我?

【问题讨论】:

  • 我现在不明白你为什么要这么做,但它几乎可以肯定是#hugemistake。如果其他人(例如您未来的自己)甚至有可能必须阅读此代码,那么您肯定会混淆他们的废话。
  • 不确定您所说的“基础是在运行时确定的”是什么意思。 Python 是一种解释型语言,一切都是在运行时确定的。
  • @allyourcode,代码示例只是为了演示结果。在允许类名和继承结构独立变化的任何情况下,我都可以达到相同的结果。我只是想知道我是否需要为了稳定而防止这种情况发生。
  • @JoelCornett,在我编写的大多数代码中,我在编辑时确定基数——即。 class Baz(Foo):pass。解释器在运行时设置基础,当然,但在我编辑文件之前,基础基本上是静态的。在这个问题中,我问的是当我编写像 return type('Baz',(Foo,) if sunny else (Bar,),{}) 这样的代码时会发生什么,其中基数由运行时条件决定。

标签: python inheritance types metaclass classname


【解决方案1】:

我无法评论您为什么要这样做,但如果您确实需要这种动态行为,或许可以考虑使用metaclass 来实现您的目标?

无论如何,如果您想知道使用相同的类名是否会在以后引起问题(我假设您的意思是除了可维护性之外的问题 - 正如其他人所说,这很有可能混乱和不必要的复杂),那么不,它不会。

documentation for type() 非常清楚这些参数的用途:

name字符串为类名,成为__name__属性; 基元组逐项列出基类并成为__bases__ 属性;并且 dict 字典是包含的命名空间 类主体的定义并成为__dict__ 属性

该描述表明该名称仅用于填充__name__ 字段。原来它基本上是针对人类的,如图in another answer

事实上,您甚至不需要使用 type() 来执行此操作 - 您可以使用简单的 class 语句来执行此操作。

class X(object): pass
class Y(object): pass

def create_other_y():
    class Y(X): pass
    global Y2
    Y2 = Y

create_other_y()

(Y, Y2)                      # => (__main__.Y, __main__.Y)
(Y.__bases__, Y2.__bases__)  # => ((object,), (__main__.X,))

【讨论】:

    【解决方案2】:

    类名不需要与您存储它的变量的名称有任何关系。您可以使用class 语句和赋值获得相同的行为:

    class Y(Base1): pass
    Y2 = Y
    class Y(Base2): pass
    

    如果您不更新返回的对象的元数据,这实际上会发生在装饰器之类的东西上:

    def decorate(klass):
        class DecoratedClass(klass):
            def stuff(self):
                do_whatever()
        return klass
    
    @decorate
    class Foo(object): pass
    
    @decorate
    class Bar(object): pass
    

    在这段代码之后,FooBar 变量引用了两个名为 DecoratedClass 的类,具有不同的继承层次结构。

    Python 对类名没有特殊意义。它主要是供人类看到的。如果你这样做,Python 不会有任何问题。不过,必须维护代码的人可能会。

    【讨论】:

    • 注意,pickle 和其他序列化方法确实会对此感到困惑,因为它们确实使用名称来尝试重构反序列化的类。无论如何,使用动态定义的类,您不太可能通过序列化获得令人满意的结果,但这是一个小问题。
    猜你喜欢
    • 2018-12-21
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 2020-11-13
    • 1970-01-01
    • 2020-02-22
    • 2017-02-24
    相关资源
    最近更新 更多