如何向现有的内置Python类型(例如字符串)添加自定义属性?

16

4
字符串的类型是 str,而不是 string - Mike Graham
你绝对可以!请看我的回答:https://dev59.com/7m445IYBdhLWcg3wytEU#58027024 - fr_andres
8个回答

24

在CPython上,您可以使用ctypes来访问解释器的C-API,这样您就可以在运行时更改内置类型。

import ctypes as c


class PyObject_HEAD(c.Structure):
    _fields_ = [
        ('HEAD', c.c_ubyte * (object.__basicsize__ -
                              c.sizeof(c.c_void_p))),
        ('ob_type', c.c_void_p)
    ]

_get_dict = c.pythonapi._PyObject_GetDictPtr
_get_dict.restype = c.POINTER(c.py_object)
_get_dict.argtypes = [c.py_object]

def get_dict(object):
    return _get_dict(object).contents.value

def my_method(self):
    print 'tada'
get_dict(str)['my_method'] = my_method

print ''.my_method()

尽管这看起来很有趣,而且也可能很有趣去解决...但不要在生产环境中使用它。只需对内置类型进行子类化或尝试找出另一种可能更符合Python风格的方法来解决问题。


+1 有用的信息!不过我很好奇,作为一个相对新手的Python程序员,为什么你不想在生产环境中这样做呢..?谢谢 - kodybrown
在修改类型的内部字典后,您还需要调用PyType_Modified以清除MRO属性缓存(或称为其他内容)。 - dlitz
3
有一个 forbiddenfruit 工具可以在多个 Python 版本中修改内置对象。 - jfs
这看起来可能有效,但实际上会导致内存损坏,因为它绕过了type.__setattr__的工作以维护内部不变量。例如,此链接的代码片段在第二个print上打印错误结果,然后在第四个print上崩溃。(forbiddenfruit也有相同的缺陷。) - user2357112
class PyObject_HEAD 是多余的。 - Joren

11

简而言之,你不能这样做。Python的方式是子类化字符串并从那里开始工作。


4
对内置类进行子类化很少有用。通常最好编写操作它们的函数,或者在需要添加一些状态时,将内置类作为一个属性创建一个新类的实例。 - Mike Graham
3
继承字典类以实现有序字典(OrderedDict)、多重字典(MultiDict)、不可变字典(ImmutableDict)或它们的组合非常普遍。然而,与字典不同,字符串并不是一种集合。 - DasIch
@MikeGraham 我也认为子类化内置类通常很麻烦(仅仅是为了添加一个新方法等)。我喜欢 .net 扩展方法在 C# 中允许所需的行为,希望 Python 中也有类似的东西。 - kodybrown
@wasatchwizard,由于Python中非绑定到任何东西的函数非常普遍,因此编写一个函数而不是扩展方法通常更容易阅读,并且冲突较少。 - Mike Graham

5

这里有一个想法。虽然并不适用于所有字符串,但它可能会有所帮助。

要设置字符串或任何其他对象的属性:

def attr(e,n,v): #will work for any object you feed it, but only that object
    class tmp(type(e)):
        def attr(self,n,v):
            setattr(self,n,v)
            return self
    return tmp(e).attr(n,v)

这里有一个例子:

>>> def helloWorld():
...     print("hello world!") #python 3
...
>>> a=attr("foo",'heloWorld',helloWorld)
>>> a
'foo'
>>> a.helloWorld()
hello world!
>>> "foo".helloWorld()
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    "foo".helloWorld()
AttributeError: 'str' object has no attribute 'helloWorld'

3

Ruby之道:

"1".to_i
"1".to_roman 

Python 方式:

int("1")
Roman("1") # or 
Roman.fromstring("1") 

Roman将在固定的内置类型列表或任何具有__int__方法的内容上工作。

这是CPython的实现限制,您无法设置内置/扩展类型的属性。这伴随着文化偏好,即避免猴子补丁,而是使用独立函数、自定义类作为所需对象的属性(甚至在罕见情况下进行子类化)。


2
为了实现这一点,您可以对str进行子类化。
然而,尽管在技术上是可能的,但大多数情况下,当您子类化内置对象(例如str)时,您正在考虑一种“具有”关系,而不是“是一个”关系,因此应该使用组合而不是继承(这意味着您应该创建一个带有字符串作为实例属性的类,而不是子类化字符串)。

我需要更深入地了解什么是实例属性,但这听起来非常明智。 - jedmao

1
你不需要这样做。使用单独的字典将信息“附加”(从逻辑上讲)到不可变值,如字符串或数字(将字符串或数字值作为键,将信息作为相应的值存储在字典中)。

0

Python不支持该功能。


0

你不能这样做,这不符合 Python 风格。在 Python 中,Monkey-patching 不是一个常用的功能,因此为了性能原因,我认为不能在其中内置的类或实例上进行 Monkey-patching。

事实上,在 Python 中它有自己的名字:duck-punching。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接