簡體   English   中英

Python:如何動態改變dict和list對象的方法?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM