"Programmatically" add stuff to a class?
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:
- The methods aren't bound when the class is initialized because they're not in the class
dict
? - I can't bind the methods manually using
__get__
because the class name isn't bound to any namespace yet.
So:
- How can I do this?
- Do I have to drop out of the class and define the dict after the class has been initialized?
- 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 staticmethod
s and classmethod
s 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.
精彩评论