繁体   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