[英]Python: How do I dynamically alter methods of dict and list objects?
這是我想要做的模型:
alist = [1,2,3,4,5] # create a vanilla python list object
replacef (alist) # replace __setitem__, extend,... with custom functions
alist[0]=2 # now the custom __setitem__ is called
這適用於DSL項目,其語法應盡可能接近普通python,因此子類化列表並使用戶調用類似alist = MyList(1,2,3,4,5)是不可取的。 此外,由於DSL需要與其他庫共存,全局更改列表和字典不是一個選項...
我已經嘗試創建instancemethods並直接在對象上設置它們,如alist.append = myFunc,但Python說這些屬性是只讀的。 似乎也不允許更改__class__
屬性。
我想在Python中嘗試做什么?
更新 :這里有一些關於對象的子類可能的一些發現:
鑒於:
class y(object):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
def f(self):
print self
print self.a
print self.b
print self.c
class x(object):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
def f(self):
print "x.f()"
>>> objy = y(1,2,3)
>>> objx = x(4,5,6)
>>> objy.f()
<__main__.y object at 0x02612650>
1
2
3
>>> objx.f()
x.f()
可以像這樣改變objx的類:
>>> objx.__class__ = y
>>> objx.f()
<__main__.y object at 0x02612D90>
4
5
6
此外,可以動態地動態添加/更改對象方法,就像在javascript中一樣:
>>> def g(self,p):
print self
print p
>>> import new
>>> objy.g = new.instancemethod(g,objy,y)
>>> objy.g(42)
<__main__.y object at 0x02612650>
42
但是,這兩種方法都會失敗,其中包含在C中實現的對象,如dict和list:
>>> def mypop(self):
print "mypop"
list.mypop(self)
>>> alist.pop = new.instancemethod(mypop,alist,list)
AttributeError: 'list' object attribute 'pop' is read-only
>>> x = [1,2,3,4,5]
>>> x.__class__ = mylist
TypeError: __class__ assignment: only for heap types
建議使用alist = replacef(alist)之類的方法在這里不起作用,因為可以從__setattribute__
調用中調用__setattribute__
如下所示:
alist = [1,2,3,4,5]
aDSLObject.items = alist # __setattribute__ calls replacef on alist
alist[0] = ....
因此,雖然確實可以動態地改變對象方法,但似乎不可能改變用C實現的對象的行為 - 或者我錯過了什么?
也許你可以在replaceref
函數里面做“alist = MyList(1,2,3,4,5)”的事情?
def replacef(l):
return MyList(l) # Return instance of list subclass that has custom __setitem__
alist = [1,2,3,4,5]
alist = replaceref(alist)
這樣,用戶在定義列表時仍會使用標准列表語法,但會在replaceref
調用后獲得新的setitem 。
“明確比隱含更好。” 您應該記錄您的用戶需要使用MyList()
而不是list()
。 從長遠來看,您和您的用戶將會更加快樂。
順便說一句,Ruby允許你想要的東西。 使用Ruby,您可以打開全局對象並添加新行為。 我發現這很可怕,Ruby運行速度比Python慢,所以我實際上並沒有敦促你切換到Ruby。
因此,提供MyList()
,並提供一種獲取現有列表並將其包裝在MyList()
。
您可以編寫MyList()
來獲取一堆參數,並非常方便地創建一個列表:
class MyList(object):
def __init__(self, *args):
self.lst = args
# add other methods to MyList() here
然后你可以使用MyList(1, 2, 3, 4)
調用它,並且實例將有一個名為lst的成員,其值為: [1, 2, 3, 4]
但是現在,如果你想從列表中創建一個MyList
實例,你是如何做到的? 如果你這樣做:
new_list = [1, 3, 5]
x = MyList(new_list)
然后你只需將x設置為: [[1, 3, 5]]
所以我建議你讓MyList()
只需要一個list參數並保存它:
def ensure_list(seq):
try:
seq[0]
return seq
except TypeError:
return list(seq)
class MyList(object):
def __init__(self, seq):
self.lst = ensure_list(seq)
# add other methods to MyList() here
我剛編輯了這個答案。 最初,我調用了list()
,現在我調用了ensure_list()
。 ensure_list()
檢查是否可以索引其參數; 如果可以的話,它既可以是列表,也可以像列表一樣,並且不會更改。 如果你不能索引它,那么ensure_list()
調用list(seq)
來嘗試將它變成一個列表。 這將與迭代器和生成器一起使用,強制它們擴展到列表。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.