将obj.method({argument:value})映射到obj.argument(value)

时间:2011-01-27 18:33:54

标签: python lambda metaprogramming

我不知道这是否有意义,但是......

我正在尝试动态地为对象分配方法。

#translate this
object.key(value)
#into this
object.method({key:value})

在我的例子中更具体一点,我有一个对象(我没有写),我们称之为motor,它有一些通用的方法集,状态和其他一些。有些人把字典作为一个参数,有些人拿一个列表。要改变电机的速度,并查看结果,我使用:

motor.set({'move_at':10})
print motor.status('velocity')

电机对象然后将此请求格式化为JSON-RPC字符串,并将其发送到IO守护程序。 python motor对象不关心参数是什么,它只是处理JSON格式和套接字。字符串move_at和velocity只是数百个有效参数中的两个。

我想做的是以下内容:

motor.move_at(10)
print motor.velocity()

我想以通用的方式做到这一点,因为我可以传递许多不同的论点。我不想做的是:

# create a new function for every possible argument
def move_at(self,x)
    return self.set({'move_at':x})
def velocity(self)
    return self.status('velocity')
#and a hundred more...

我做了一些搜索,这表明解决方案在于lambdas和元编程,这两个主题我无法理解。

更新:

根据user470379的代码,我提出了以下内容......

# This is what I have now....
class Motor(object):
    def set(self,a_dict):
        print "Setting a value", a_dict
    def status(self,a_list):
        print "requesting the status of", a_list
        return 10

# Now to extend it....
class MyMotor(Motor):
    def __getattr__(self,name):
        def special_fn(*value):
            # What we return depends on how many arguments there are.
            if len(value) == 0: return self.status((name))
            if len(value) == 1: return self.set({name:value[0]})
        return special_fn
    def __setattr__(self,attr,value): # This is based on some other answers
        self.set({attr:value})


x = MyMotor()
x.move_at = 20 # Uses __setattr__
x.move_at(10)  # May remove this style from __getattr__ to simplify code.
print x.velocity()

输出:

Setting a value {'move_at': 20}
Setting a value {'move_at': 10}
10

感谢所有帮助过的人!

4 个答案:

答案 0 :(得分:4)

为返回动态创建的函数的类创建自己的__getattr__怎么样? IIRC,在__getattr____getattribute__之间需要注意的一些棘手的案例,我不记得我的头脑,我相信有人会发表评论提醒我:

def __getattr__(self, name):
    def set_fn(self, value):
        return self.set({name:value})
    return set_fn

那么应该发生的是调用一个不存在的属性(即:move_at)将调用__getattr__函数并创建一个将返回的新函数(set_fn以上)。该函数的name变量将绑定到name传递给__getattr__的参数(在本例中为"move_at")。然后将使用您传递的参数调用该新函数(在这种情况下为10)。

修改

使用lambdas(未经测试)的更简洁版本:

def __getattr__(self, name):
    return lambda value: self.set({name:value})

答案 1 :(得分:1)

对此有很多不同的潜在答案,但其中许多可能涉及对对象进行子类化和/或编写或覆盖__getattr__函数。

基本上,只要python无法以通常的方式找到属性,就会调用__getattr__函数。

假设你可以继承你的对象,这里有一个你可能会做的简单例子(它有点笨拙,但它是一个开始):

class foo(object):
    def __init__(self):
        print "initting " + repr(self)
        self.a = 5

    def meth(self):
        print self.a

class newfoo(foo):
    def __init__(self):
        super(newfoo, self).__init__()
        def meth2():                           # Or, use a lambda: ...
            print "meth2: " + str(self.a)      # but you don't have to
        self.methdict = { "meth2":meth2 }

    def __getattr__(self, name):
        return self.methdict[name]

f = foo()
g = newfoo()

f.meth()
g.meth()
g.meth2()

输出:

initting <__main__.foo object at 0xb7701e4c>
initting <__main__.newfoo object at 0xb7701e8c>
5
5
meth2: 5

答案 2 :(得分:1)

您似乎拥有可以通过

设置的对象的某些“属性”
obj.set({"name": value})

并通过

查询
obj.status("name")

Python中常见的方法是将此行为映射到简单的属性访问。所以我们写了

obj.name = value

设置属性,我们只需使用

obj.name

查询它。这可以使用__getattr__()__setattr__()特殊方法轻松实现:

class MyMotor(Motor):
    def __init__(self, *args, **kw):
        self._init_flag = True
        Motor.__init__(self, *args, **kw)
        self._init_flag = False            
    def __getattr__(self, name):
        return self.status(name)
    def __setattr__(self, name, value):
        if self._init_flag or hasattr(self, name):
            return Motor.__setattr__(self, name, value)
        return self.set({name: value})

请注意,此代码不允许在初始化后动态创建Motor个实例的新“实际”属性。如果需要,可以在__setattr__()实现中添加相应的例外。

答案 3 :(得分:1)

不要使用函数调用语法进行设置,而应考虑使用赋值(使用=)。同样,只需使用属性语法来获取值,而不是函数调用语法。然后你可以use __getattr__ and __setattr__

class OtherType(object):  # this is the one you didn't write
  # dummy implementations for the example:
  def set(self, D):
    print "setting", D
  def status(self, key):
    return "<value of %s>" % key

class Blah(object):
  def __init__(self, parent):
    object.__setattr__(self, "_parent", parent)

  def __getattr__(self, attr):
    return self._parent.status(attr)
  def __setattr__(self, attr, value):
    self._parent.set({attr: value})

obj = Blah(OtherType())
obj.velocity = 42   # prints setting {'velocity': 42}
print obj.velocity  # prints <value of velocity>