将方法动态附加到使用swig生成的现有Python对象?

时间:2009-09-05 09:30:59

标签: python class dynamic methods

我正在使用Python类,并且我没有对其声明的写入权限。 如何在不修改类声明的情况下将自定义方法(例如 __str__ )附加到从该类创建的对象中?

编辑: 谢谢你的所有答案。我尝试了所有这些,但他们没有解决我的问题。这是一个最小的例子,我希望能澄清这个问题。我使用swig来包装C ++类,目的是覆盖swig模块返回的一个对象__str__函数。我使用cmake来构建示例:

test.py

import example

ex = example.generate_example(2)

def prnt(self):
    return str(self.x)

#How can I replace the __str__ function of object ex with prnt?
print ex
print prnt(ex)

example.hpp

struct example
{
    int x;
};

example generate_example(int x);

example.cpp

#include "example.hpp"
#include <iostream>

example generate_example(int x)
{
    example ex;
    ex.x = x;
    return ex;
}

int main()
{
    example ex = generate_example(2);
    std::cout << ex.x << "\n";
    return 1;
}

example.i

%module example

%{
#include "example.hpp"
%}

%include "example.hpp"

的CMakeLists.txt

cmake_minimum_required(VERSION 2.6)

find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})

find_package(PythonLibs)
include_directories(${PYTHON_INCLUDE_PATH})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

set_source_files_properties(example.i PROPERTIES CPLUSPLUS ON)
swig_add_module(example python example.i example)
swig_link_libraries(example ${PYTHON_LIBRARIES})

if(APPLE)
    set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS "${CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS} -flat_namespace")
endif(APPLE)

要构建并运行test.py,请复制目录中的所有文件,并在该目录中运行

cmake .
make 
python test.py

这导致以下输出:

<example.example; proxy of <Swig Object of type 'example *' at 0x10021cc40> >
2

正如您所看到的,Swig对象具有自己的 str 功能,这就是我想要覆盖的内容。

5 个答案:

答案 0 :(得分:20)

如果您创建了一个包装类,这将适用于任何其他类,无论是内置还是非内置类。这称为“包含和委托”,它是继承的常见替代方法:

class SuperDuperWrapper(object):
    def __init__(self, origobj):
        self.myobj = origobj
    def __str__(self):
        return "SUPER DUPER " + str(self.myobj)
    def __getattr__(self,attr):
        return getattr(self.myobj, attr)

__getattr__方法会将SuperDuperWrapper对象上的所有未定义属性请求委托给包含的myobj对象。事实上,考虑到Python的动态类型,你可以使用这个类来SuperDuper'包装任何东西:

s = "hey ho!"
sds = SuperDuperWrapper(s)
print sds

i = 100
sdi = SuperDuperWrapper(i)
print sdi

打印:

SUPER DUPER hey ho!
SUPER DUPER 100

在您的情况下,您将从无法修改的函数中获取返回的对象,并将其包装在您自己的SuperDuperWrapper中,但您仍然可以像访问基础对象一样访问它。

print sds.split()
['hey', 'ho!']

答案 1 :(得分:3)

>>> class C(object):
...     pass
... 
>>> def spam(self):
...     return 'spam'
... 
>>> C.__str__ = spam
>>> print C()
spam

它不适用于使用__slots__的类。

答案 2 :(得分:3)

创建一个子类。例如:

>>> import datetime
>>> t = datetime.datetime.now()
>>> datetime.datetime.__str__ = lambda self: 'spam'
...
TypeError: cant set attributes of built-in/extension type 'datetime.datetime'
>>> t.__str__ = lambda self: 'spam'
...
AttributeError: 'datetime.datetime' object attribute '__str__' is read-only
>>> class mydate(datetime.datetime):
    def __str__(self):
        return 'spam'

>>> myt = mydate.now()
>>> print t
2009-09-05 13:11:34.600000
>>> print myt
spam

答案 3 :(得分:2)

这是another question的答案:

import types
class someclass(object):
    val = "Value"
    def some_method(self):
        print self.val

def some_method_upper(self):
    print self.val.upper()

obj = someclass()
obj.some_method()

obj.some_method = types.MethodType(some_method_upper, obj)
obj.some_method()

答案 4 :(得分:1)

请注意,使用Alex的子类构思,您可以使用“from ... import ... as”来帮助自己更多:

from datetime import datetime as datetime_original

class datetime(datetime_original):
   def __str__(self):
      return 'spam'

所以现在该类具有标准名称,但行为不同。

>>> print datetime.now()
    'spam'

当然,这可能很危险......