【问题标题】:"Programmatically" add stuff to a class?“以编程方式”向课程添加内容?
【发布时间】:2011-08-05 01:47:29
【问题描述】:

我正在编写一个包含int 到方法映射的dict 的类。但是,在此 dict 中设置值会导致 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__吗?

更新0

方法会这样调用:

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

另外关于数字索引/列表:并非所有索引都会出现。我不知道可以在 Python 中执行 list([1]=a, [2]=b, [1337]=leet),是否有等价的?我应该分配一个任意长度的列表并设置特定的值吗?我在这里唯一感兴趣的是最小化查找时间,它与O(1) 散列{} 真的有那么不同吗?我暂时忽略了这一点,因为它是过早的优化。

【问题讨论】:

  • 带有数字索引的字典?你忘了列表吗?
  • @Matt Ball:请看更新,并传授相关智慧谢谢。
  • @Matt Joiner:看看我更新的答案。你在做这个吗?
  • 你不需要这样做。看我的回答。

标签: python class dictionary initialization python-3.x


【解决方案1】:

我不确定为什么你正在做你正在做的事情,但你肯定可以在类定义中做到这一点;你不需要__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作为变量名,它也是一个内置的。

字典绝对适合这类事情。

编辑:如果您使用新型类,这也适用于 staticmethods 和 classmethods。

【讨论】:

  • 这个问题是 m, 在这里是一个类变量而不是一个实例变量。
  • @Mo Zo:似乎没有一个很好的解决方案。这似乎是在类声明期间使用类方法的代价。
  • m 是类变量还是实例变量有什么关系?它仍然可以将self 传递给实例方法...
【解决方案2】:

首先不要使用变量“map”,因为会获取python函数map的构建。

您需要有 init 方法并在 init 方法中使用 self 初始化您的字典。现在的字典只是类的一部分,而不是类实例的一部分。如果您希望类的实例也具有字典,则需要创建一个 init 方法并在那里初始化您的字典。所以你需要这样做:

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

或者如果你想让字典成为一个类变量,那么这个:

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

【讨论】:

  • 如果字典不根据实例而改变,将其保留为类变量可能没问题。
  • 对于每个实例,绑定所有方法(有很多)可能代价高昂。
  • 您希望字典是类变量还是实例变量?因为在 agf 的回答中,字典是一个类变量
【解决方案3】:

您要做什么并不完全清楚。我怀疑你想写类似的代码

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

【讨论】:

  • 方式矫枉过正,他可以做到f.mapping[i](f)
  • agf:这可能有点矫枉过正,但它解决了他遇到的问题,也很有趣。我想我也更喜欢接受的答案。
【解决方案4】:

这对我来说似乎有点令人费解。最终目标是什么?

如果您真的想这样做,您可以利用这些方法已经包含在映射中的事实 (__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)

这种模式存在于一些现有的库模块中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-18
    • 2012-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多