2009-09-05 45 views
8

我正在使用Python類,並且我沒有對其聲明的寫入權限。 如何在不修改類聲明的情況下將自定義方法(如__str__)附加到從該類創建的對象?編輯: 謝謝你的所有答案。我嘗試了所有,但他們還沒有解決我的問題。這是我希望澄清問題的一個最小例子。我正在使用swig來包裝一個C++類,目的是覆蓋swig模塊返回的對象的__str__函數。我使用的cmake構建示例:將方法動態附加到使用swig生成的現有Python對象?

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函數,這就是我想重寫的。

+0

其他StackOverflow的問題:http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object – 2009-09-05 11:43:44

回答

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'ly包裹任何內容:

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!'] 
+0

在我的Python職業生涯中,我很早就使用這種技術爲遠程CORBA對象編寫了一個代理攔截器 - 客戶希望一個特定的遠程方法稍微增強,但否則其他一切都會表現相同。直到最近剛剛從C++轉換而來,我對編寫代碼很少,而不是C++會讓我做什麼感到震驚。 – PaulMcG 2009-09-05 16:03:48

+0

謝謝保羅!這正是我所期待的。 – dzhelil 2009-09-05 16:15:03

+0

這種方法的問題是生成的包裝對象不可用,這有時是一個大問題 – 2011-09-15 07:22:24

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

它不適用於使用__slots__的類。

+0

不幸的是,這是不是在我的情況下工作。我試圖覆蓋的類是使用swig從C++頭文件自動生成的。 – dzhelil 2009-09-05 09:52:54

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 
+1

+1:這是繼承存在的根本原因。 – 2009-09-05 11:13:57

+0

感謝您的建議亞歷克斯。確實,我可以很容易地創建一個子類,但是這對我來說幫不了多少忙,因爲我正在使用的對象是由我無法修改的函數返回的。 在您的示例中,該對象的類型是datetime。我想覆蓋它的'__str__'函數,所以爲了實現這個功能,我需要一種將datetime對象轉換爲mydate的方法。 – dzhelil 2009-09-05 14:46:37

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() 
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' 

當然,這可能是危險的...

相關問題