开发者

"Programmatically" add stuff to a class?

开发者 https://www.devze.com 2023-03-26 14:35 出处:网络
I\'m writing a class that has a dict containing int to method mappings. However setting the values in this dict results in the dict being populated with unbound functions.

I'm writing a class that has a dict containing int to method mappings. However setting the values in this dict results in the dict being populated with unbound functions.

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

This doesn't work for a few reasons:

  1. The methods aren't bound when the class is initialized because they're not in the class dict?
  2. I can't bind the methods manually using __get__ because the class name isn't bound to any namespace yet.

So:

  1. How can I do this?
  2. Do I have to drop out of the class and define the dict after the class has been initialized?
  3. Is it really necessary to call __get__ on the methods to bind them?

Update0

The methods will be called like this:

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

Also regarding the numeric indices/list: Not all indices will be present. I'm not aware that one can do list([1]=a, [2]=b, [1337]=leet) in Python, is there an equivalent? Should I just allocate a arbitrary length list and set specific values? The only interest I have here is in minimizing the lookup time, would it really be that different to the O(1) ha开发者_C百科sh that is {}? I've ignored this for now as premature optimization.


I'm not sure exactly why you're doing what you're doing, but you certainly can do it right in the class definition; you don't need __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)

An "unbound" instance method simply needs you to pass it an instance of the class manually, and it works fine.

Also, please don't use int as the name of a variable, either, it's a builtin too.

A dictionary is absolutely the right type for this kind of thing.

Edit: This will also work for staticmethods and classmethods if you use new-style classes.


First of all Don't use variable "map" since build in python function map will be fetched.

You need to have init method and initialize your dictionary in the init method using self. The dictionary right now is only part of the class, and not part of instances of the class. If you want instances of the class to have the dictionary as well you need to make an init method and initialize your dictionary there. So you need to do this:

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

or if you want the dictionary to be a class variable, then this:

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


It's not totally clear just what you're trying to do. I suspect you want to write code something like

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"

so, that methods attribute needs to return a bound instance method somehow, but without being called directly. This is a good opportunity to use a descriptor. We need to do something that will return a special object when accessed through an instance, and we need that special object to return a bound method when indexed. Let's start from the inside and work our way out.

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

We're just implementing the barest minimum of the mapping protocol, and deferring completely to an underlying collection. here we make use of types.MethodType to get a real instance method when needed, including binding something that's already an instance method. We'll see how that's useful in a minute.

We could implement a descriptor directly, but for the purposes here, property already does everything we need out of a descriptor, so we'll just define one that returns a properly constructed BindMapping instance.

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

Just for kicks, we also throw in some extra methods outside the class body. Notice how the the methods added inside the class body are functions, just like functions added outside; methods added outside the class body are actual instance methods (although unbound).

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

And it works as advertised:

>>> f = Foo()
>>> for i in range(len(f._mapping)):
...     f.mapping[i]()
... 
1
2
3
1
>>> 


This seems kind of convoluted to me. What is the ultimate goal?

If you really want do to this, you can take advantage of the fact that the methods are alreaday contained in a mapping (__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)

This pattern is found in some existing library modules.

0

精彩评论

暂无评论...
验证码 换一张
取 消