Given a class MyClass(object)
, how can I programmatically define class member methods using some template and the MyClass.__getattr__
mechanism? I'm thinking of something along the lines of
class MyClass(object):
def __init__(self, defaultmembers, members):
# defaultmembers is a list
# members is a dict
self._defaultmembers = defaultmembers
self._members = members
# some magic spell creating a member function factory
def __getattr__(self):
# some more magic
def f_MemberB():
pass
C = MyClass(defaultmembers=["MemberA"], members=dict(MemberB=f_MemberB)
C.MemberA() # should be a valid statement
C.MemberB() # should also be a valid statement
C.MemberC() # should raise an AttributeError
C.MemberA
should be a method automatically created from some template mechanism inside the class, and C.MemberB
should be the function f_MemberB
.
You don't need to redefine __getattr__
(and in fact you generally should never do that). Python is a late binding language. This means you can simply assign values to names in a class at any time (even dynamically at runtime) and they will now exist.
So, in your case, you can simply do:
class MyClass(object):
def __init__(self, defaultmembers, members):
# defaultmembers is a list
# members is a dict
for func in defaultmembers:
setattr(self, func.__name__, func)
for name, func in members.items():
setattr(self, name, func)
Note that this will not actually bind the method to the class (it will not get self as its first argument). If that is what you want, you need to use the MethodType function from types
, like so:
from types import MethodType
class MyClass(object):
def __init__(self, defaultmembers, members):
# defaultmembers is a list
# members is a dict
for func in defaultmembers:
name = func.__name__
setattr(self, name , func)
setattr(self, name , MethodType(getattr(self, name), self))
for name, func in members.items():
setattr(self, name, func)
setattr(self, name , MethodType(getattr(self, name), self))
Example:
def def_member(self):
return 1
def key_member(self):
return 2
>>> test = MyClass([def_member], {'named_method':key_member})
>>> test.def_member()
1
>>> test.named_method()
2
You can also make the init method slightly less awkward by using *args
and **kwargs
, so that the example would just be test = MyClass(def_member, named_member = key_member)
if you know there won't be any other arguments to the constructor of this class.
Obviously I've left out the template creation bit for the defaultmembers
, since I've used passing a function, rather than simply a name, as the argument. But you should be able to see how you would expand that example to suit your needs, as the template creation part is a bit out of the scope of the original question, which is how to dynamically bind methods to classes.
An important note: This only affects the single instance of MyClass
. Please alter the question if you want to affect all instances. Though I would think using mixin class would be better in that case.