«Программно» добавить материал в класс? - PullRequest
0 голосов
/ 05 августа 2011

Я пишу класс, который содержит dict, содержащий int для сопоставления методов. Однако установка значений в этом dict приводит к тому, что оно заполняется несвязанными функциями.

class A:
    def meth_a: ...
    def meth_b: ...
    ...
    map = {1: meth_a, 2: meth_b, ...}
    for int in ...:
        map[int] = meth_x

Это не работает по нескольким причинам:

  1. Методы не связаны при инициализации класса, потому что они не в классе dict?
  2. Я не могу связать методы вручную, используя __get__, потому что имя класса еще не привязано ни к какому пространству имен.

Итак:

  1. Как я могу это сделать?
  2. Должен ли я бросить класс и определить диктат после того, как класс был инициализирован?
  3. Действительно ли необходимо вызывать __get__ для методов, чтобы связать их?

Update0

Методы будут называться так:

def func(self, int):
    return self.map[int]()

Также относительно числовых индексов / списка: не все индексы будут присутствовать. Я не знаю, что можно сделать list([1]=a, [2]=b, [1337]=leet) в Python, есть ли эквивалент? Должен ли я просто выделить список произвольной длины и установить конкретные значения? Единственный интерес, который у меня есть, заключается в том, чтобы минимизировать время поиска, действительно ли оно будет отличаться от хеша O(1), который равен {}? Я пока игнорировал это как преждевременную оптимизацию.

Ответы [ 4 ]

3 голосов
/ 05 августа 2011

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

class A:
    def meth_a(self): pass

    m = {1: meth_a}
    def foo(self, number):
        self.m[number](self)

a = A()
a.foo(1)

"несвязанному" методу экземпляра просто нужно, чтобы вы передали ему экземпляр класса вручную, и он отлично работает.

Кроме того, пожалуйста, , не используйте int в качестве имени переменной, это тоже встроенное значение.

Словарь абсолютно правильный тип для этого видавещей.

Редактировать: Это также будет работать для staticmethod с и classmethod с, если вы используете классы нового стиля.

2 голосов
/ 05 августа 2011

Не совсем понятно, что вы пытаетесь сделать. Я подозреваю, что вы хотите написать код что-то вроде

class Foo(object):
    def __init__(self, name):
        self.name = name

    def method_1(self, bar):
        print self.name, bar

    # ... something here

my_foo = Foo('baz')
my_foo.methods[1]('quux')
# prints "baz quux"

так, что атрибут methods должен каким-то образом возвращать связанный метод экземпляра, но без непосредственного вызова. Это хорошая возможность использовать descriptor. Нам нужно сделать что-то, что будет возвращать специальный объект при доступе через экземпляр, и нам нужен этот специальный объект, чтобы возвращать связанный метод при индексации. Давайте начнем изнутри и продолжим наш путь.

>>> import types
>>> class BindMapping(object):
...     def __init__(self, instance, mapping):
...         self.instance, self.mapping = instance, mapping
...     
...     def __getitem__(self, key):
...         func = self.mapping[key]
...         if isinstance(func, types.MethodType):
...             return types.MethodType(func.im_func, self.instance, func.im_class)
...         else:
...             return types.MethodType(func, self.instance, type(self))
... 

Мы просто реализуем минимальный протокол сопоставления и полностью откладываем базовую коллекцию. здесь мы используем types.MethodType для получения реального метода экземпляра, когда это необходимо, включая привязку чего-то, что уже является методом экземпляра. Посмотрим, как это будет полезно через минуту.

Мы могли бы реализовать дескриптор напрямую, но для целей здесь, property уже делает все, что нам нужно, из дескриптора, поэтому мы просто определим тот, который возвращает правильно построенный экземпляр BindMapping.

>>> class Foo(object):
...     def method_1(self):
...         print "1"
...     def method_2(self):
...         print "2"
...     
...     _mapping = [method_1, method_2]
...     
...     @property
...     def mapping(self):
...         return BindMapping(self, self._mapping)
... 

Только для пинка мы также добавляем несколько дополнительных методов вне тела класса. Обратите внимание, что методы, добавленные внутри тела класса, являются функциями, точно так же как функции, добавленные снаружи; методы, добавленные вне тела класса, являются фактическими методами экземпляра (хотя и не связаны).

>>> def method_3(self):
...     print "3"
... 
>>> Foo._mapping.append(method_3)
>>> Foo._mapping.append(Foo.method_1)
>>> map(type, Foo._mapping)
[<type 'function'>, <type 'function'>, <type 'function'>, <type 'instancemethod'>]

И это работает как рекламируется:

>>> f = Foo()
>>> for i in range(len(f._mapping)):
...     f.mapping[i]()
... 
1
2
3
1
>>> 
2 голосов
/ 05 августа 2011

Прежде всего, не используйте переменную "map", так как сборка в карте функций Python будет получена.

Вы должны иметь метод init и инициализировать свой словарь в методе init, используя self. Словарь сейчас является только частью класса, а не частью экземпляров класса. Если вы хотите, чтобы экземпляры класса также имели словарь, вам нужно создать метод init и инициализировать ваш словарь. Так что вам нужно сделать это:

def __init__(self):
    self.mymap[int] = self.meth_x

или если вы хотите, чтобы словарь был переменной класса, то это:

def __init__(self):
    A.mymap[int] = self.meth_x
1 голос
/ 05 августа 2011

Это кажется мне запутанным.Какова конечная цель?

Если вы действительно хотите сделать это, вы можете воспользоваться тем, что методы уже содержатся в отображении (__dict__).

class A(object):

    def meth_1(self):
        print("method 1")

    def meth_2(self):
        print("method 2")

    def func(self, i):
        return getattr(self, "meth_{}".format(i))()


a = A()
a.func(2)

Этот шаблон находится внекоторые существующие библиотечные модули.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...