为什么我在创建从str继承的类(或从int)
时遇到问题class C(str):
def __init__(self, a, b):
str.__init__(self,a)
self.b = b
C("a", "B")
TypeError: str() takes at most 1 argument (2 given)
如果我尝试使用int
而不是str
,也会发生相同的情况,但它适用于自定义类。我需要使用__new__
代替__init__
吗?为什么呢?
答案 0 :(得分:66)
>>> class C(str):
... def __new__(cls, *args, **kw):
... return str.__new__(cls, *args, **kw)
...
>>> c = C("hello world")
>>> type(c)
<class '__main__.C'>
>>> c.__class__.__mro__
(<class '__main__.C'>, <type 'str'>, <type 'basestring'>, <type 'object'>)
由于在构造对象之后调用__init__
,因此修改不可变类型的值为时已晚。请注意,__new__
是一种类方法,因此我调用了第一个参数cls
有关详细信息,请参阅here
>>> class C(str):
... def __new__(cls, value, meta):
... obj = str.__new__(cls, value)
... obj.meta = meta
... return obj
...
>>> c = C("hello world", "meta")
>>> c
'hello world'
>>> c.meta
'meta'
答案 1 :(得分:11)
继承内置类型很少值得。你必须处理好几个问题而且你并没有获得太多的好处。
使用构图几乎总是更好。您可以将str
对象保留为属性,而不是继承str
。
class EnhancedString(object):
def __init__(self, *args, **kwargs):
self.s = str(*args, **kwargs)
您可以使用str
手动或自动推迟您想要在基础self.s
__getattr__
上使用的任何方法。
话虽这么说,需要你自己的字符串类型应该让你暂停。有许多类应该将字符串存储为其主要数据,但是您通常希望使用str
或unicode
(后者,如果您要表示文本)来表示字符串的一般表示。 (一个常见的例外是如果您需要使用UI工具包的字符串类型。)如果您想为字符串添加功能,请尝试使用对字符串而不是进行操作的函数>用作字符串的新对象,它可以使您的代码更简单,并与其他人的程序更兼容。
答案 2 :(得分:8)
实例化一个类时,传入的参数将传递给__new__
(构造函数),然后传递给类的__init__
(初始化程序)方法。因此,如果从一个对实例化期间可能提供的参数数量有限制的类继承,则必须保证其__new__
及其__init__
都不会获得比预期更多的参数。这就是你遇到的问题。您使用C("a", "B")
实例化您的班级。解释器在__new__
中查找C
方法。 C
没有它,所以python窥视它的基类str
。因为它有一个,所以使用并提供两个参数。但是str.__new__
期望只获得一个参数(除了它的类对象作为第一个参数)。所以TypeError
被提出来了。这就是为什么你必须在你的子类中扩展它,就像你使用__init__
一样。但请记住,它必须返回类实例并且它是静态方法(无论是否使用@staticmethod
装饰器定义),如果使用{{1}功能。
答案 3 :(得分:5)
在不可变类型的情况下使用__new__
:
class C(str):
def __new__(cls, content, b):
return str.__new__(cls, content)
def __str__(self):
return str.__str__(self)
a=C("hello", "world")
print a
打印返回hello
。
Python字符串是不可变类型。调用函数__new__
以创建对象C
的新实例。 python __new__
函数基本上存在,允许从不可变类型继承。
答案 4 :(得分:4)
仔细阅读this后,这是继承str的另一种尝试。其他答案的更改是使用super(TitleText, cls).__new__
在正确的类中创建实例。无论何时使用它,这个似乎都像str一样,但允许我覆盖一个方法:
class TitleText(str):
title_text=""
def __new__(cls,content,title_text):
o=super(TitleText, cls).__new__(cls,content)
o.title_text = title_text
return o
def title(self):
return self.title_text
>>> a=TitleText('name','A nice name')
>>> a
'name'
>>> a[0]
'n'
>>> a[0:2]
'na'
>>> a.title()
'A nice name'
这使您可以正确地进行切片和下标。这是用来干嘛的?用于在管理员索引页面中重命名Django应用程序。
答案 5 :(得分:0)
上面已经回答了这个问题,这只是一个切线观察,可能对某人有用。
当我试图找出一种方法来删除临时文件时,我遇到了这个问题,因为它被引用的字典被删除了。
上下文是一个 Flask 会话:用户可以上传一些文件,但在有效提交将他/她的数据传输到最终目的地所必须经历的整个工作流程之前就放弃了。在那之前,我将文件保存在一个临时目录中。假设用户放弃并关闭浏览器窗口,我不希望这些文件挥之不去。
由于我在 Flask 会话中保留了临时路径——它只是一个最终会被删除(例如超时)的字典,我可以自定义一个 str
类来保存临时目录地址/路径,并且使用其 __del__
方法处理临时目录删除。
这里是:
class Tempdir(str):
def __new__(cls, *args, **kwargs):
from tempfile import mkdtemp
_dir = mkdtemp()
return super().__new__(cls, _dir)
def __del__(self):
from shutil import rmtree
rmtree(str(self))
在你的 python 解释器/应用程序中实例化它:
> d = Tempfile()
> d
'/var/folders/b1/frq3gywj3ljfqrf1yc7zk06r0000gn/T/tmptwa_g5fw'
>
> import os
> os.path.exists(d)
True
当你退出解释器时:
$ ls /var/folders/b1/frq3gywj3ljfqrf1yc7zk06r0000gn/T/tmptwa_g5fw
ls: /var/folders/b1/frq3gywj3ljfqrf1yc7zk06r0000gn/T/tmptwa_g5fw: No such file or directory
给你。