Отображение obj.method ({аргумент: значение}) в obj.argument (значение) - PullRequest
4 голосов
/ 27 января 2011

Не знаю, будет ли это иметь смысл, но ...

Я пытаюсь динамически назначать методы объекту.

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

Чтобы быть более конкретным в моем примере, у меня есть объект (который я не писал), давайте назовем его мотором, у которого есть набор общих методов, состояние и некоторые другие. Некоторые принимают словарь в качестве аргумента, а некоторые - список. Чтобы изменить скорость двигателя и увидеть результат, я использую:

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

Объект motor затем форматирует этот запрос в строку JSON-RPC и отправляет его демону ввода-вывода. Моторный объект python не заботится о аргументах, он просто обрабатывает JSON-форматирование и сокеты. Строки move_at и скорость - это только два из сотен допустимых аргументов.

Вместо этого я хотел бы сделать следующее:

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...

Я провел некоторые поиски по этому вопросу, которые предположили, что решение лежит в лямбдах и метапрограммировании, двух предметах, которые я не смог осознать.

UPDATE:

На основе кода от 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 ]

4 голосов
/ 27 января 2011

А как насчет создания собственного __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).

Редактировать

Более краткая версия с использованием лямбды (не проверено):

def __getattr__(self, name):
    return lambda value: self.set({name:value})
1 голос
/ 27 января 2011

Вместо установки с использованием синтаксиса вызова функций рассмотрите возможность использования присваивания (с =).Аналогично, просто используйте синтаксис атрибута для получения значения вместо синтаксиса вызова функции.Тогда вы можете использовать __getattr__ и __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>
1 голос
/ 27 января 2011

У вас, кажется, есть определенные "свойства" вашего объекта, которые можно установить с помощью

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})

Обратите внимание, что этот код запрещает динамическое создание нового "реального"msgstr "атрибуты Motor экземпляров после инициализации.Если это необходимо, соответствующие исключения могут быть добавлены к реализации __setattr__().

1 голос
/ 27 января 2011

Существует множество различных возможных ответов на этот вопрос, но многие из них, вероятно, будут включать создание подкласса объекта и / или написание или переопределение функции __getattr__.

По сути, функция __getattr__ вызывается всякий раз, когда python не может найти атрибут обычным способом.

Предполагая, что вы можете создать подкласс для своего объекта, вот простой пример того, что вы можете сделать (это немного неуклюже, но это только начало):

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
...