【问题标题】:Sub-class of String, when called, binds argument to both parameters of __init__?String 的子类在调用时将参数绑定到 __init__ 的两个参数?
【发布时间】:2015-08-02 21:22:57
【问题描述】:

在 Python 2.7 中,以下 class 语句在运行时使用 一个字符串作为其参数,似乎将该字符串绑定到两者 init 的参数('self' 和 'grasshopper')。

可以定义看似相同的方法 self 和 self.grasshopper 的结果。

我注意到,如果 init 被定义为采用三个参数——例如, 'self'、'grasshopper' 和 'beetle' -- 类,当 使用两个字符串参数运行(例如 CrazyString('spam', 'eggs')),会引发错误:

TypeError: str() takes at most 1 argument (2 given)

这表明'str'不仅仅是被继承的 由 CrazyString 类执行,但作为函数执行。

我不明白发生了什么,因此我觉得 不确定我是否应该编写对自身进行操作的方法 或在 self.grasshopper 上。或者实际上是 self 和 self.grasshopper 一样吗?

class CrazyString(str):

    def __init__(self, grasshopper):
        self.grasshopper = grasshopper

    def backwards1(self):
        return self.grasshopper[::-1]

    def backwards2(self):
        return self[::-1]

if __name__ == "__main__":
    cs = CrazyString('spam')
    print cs
    print cs.grasshopper
    print cs.backwards1()
    print cs.backwards2()

运行时('python crazy.py'),我得到以下输出:

spam
spam
maps
maps

【问题讨论】:

  • hmm...既然没人会问,为什么你首先要继承 str ?
  • @user689003,我想向已经了解字符串、列表和字典的本科生说明 Python OOP 的主要思想——自我和继承。像字符串这样的子类化让我们以一种足够简单的方式专注于自我和继承,让他们编写一些基本方法,然后在六月学期结束之前学习编写单元测试以添加一两个更多方法。
  • 建议您只使用任意类,而不是字符串。正如您所看到的,从str 继承并不是人们希望的那么简单,__new__ 的概念是非常特定于 python 的,不一定与 OOP 相关 + 启动时可能会令人困惑!那么你有关于str 的交易是不可变的,我敢肯定这会导致一些头发拉扯
  • @user3012759 谢谢 - 我听从了你的建议,在介绍 OOP 时放弃了使用 str 子类的想法。

标签: python class python-2.7 oop


【解决方案1】:

这个答案有两个部分:解释str 初始化发生的位置(因为您不调用str.__init__),以及解释“str 存储字符串的位置”。

首先,由于str 是用C 实现的不可变类型,str.__init__ 实际上并不参与初始化字符串。还有另一种方法__new__,在__init__ 初始化它们之前,Python 调用它来创建一个类型的实例,对于像str 这样的不可变类型,__new__ 会创建预初始化的对象。 (毕竟,如果__init__ 初始化了字符串,那将是一个突变。)

其次,self 似乎是一个字符串,因为它假定是一个字符串。你继承自str,所以你的类的实例是str的实例;他们是字符串!你可以print 像字符串一样对它们进行切片反转,因为它们从str 继承了所有这些。您不需要像人们有时期望的那样使用 self.str 之类的“获取底层字符串”。

【讨论】:

  • 这个解释非常有帮助。我试图根据您的解释用代码回复这里,但看到代码示例不适合这里——见上文。
【解决方案2】:

在您的情况下,您偶然发现了 __new__ 函数,这给您带来了麻烦。

你应该做类似(未经测试!)

class CrazyString(str):

    def __new__(cls, grasshopper, beetle):
        return cls(grasshopper) # not sure if that works, maybe we have to use super() in any way...

    def __init__(self, grasshopper, beetle):
        self.grasshopper = grasshopper
        self.beetle = grasshopper

    def backwards1(self):
        return self.grasshopper[::-1]

    def backwards2(self):
        return self[::-1]

    def backwards3(self):
        return self.beetle[::-1]

if __name__ == "__main__":
    cs = CrazyString('spam')
    print cs
    print cs.grasshopper
    print cs.backwards1()
    print cs.backwards2()
    print cs.backwards3()

【讨论】:

  • 在我的情况下,我实际上不需要传递多个字符串作为参数,并且真的尝试过只是为了看看尝试是否会提供任何有用的线索。但这很有趣 - 谢谢!
【解决方案3】:

所以字符串 'spam' 在作为第二个参数传递给__init__ 之前就被__new__ 方法变成了一个'str' 类型的对象。

如果__init__ 的参数数量不超过两个,则调用CrazyString('spam') 将失败并以TypeError 退出。这有效:

class CrazyString(str):
    def backwards(self):
        return self[::-1]

if __name__ == "__main__":
    cs = CrazyString('spam')
    print cs
    print cs.backwards()

【讨论】:

  • 在这里删除那个无用的__init__()方法。
  • 谢谢@BlackJack - 删除__init__() 有效。所以它可以在没有__init__()__init__() 和参数的属性数量的情况下工作,但是如果参数数量错误,则无法调用该类(引发异常)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
  • 2012-03-22
  • 2020-11-13
相关资源
最近更新 更多