Python : Allowing methods not specifically defined to be called ala __getattr__
I'm trying to write a Python class that has the ability to do the following:
开发者_C百科c = MyClass()
a = c.A("a name for A") # Calls internally c.create("A", "a name for A")
b = c.B("a name for B") # Calls internally c.create("B", "a name for B")
A and B could be anything (well, they're defined in a database, but I don't want to explicitly define them in my code)
A hacky workaround for it would be to do the following:
class MyClass():
def __init__(self):
self.createItem = ""
def create(self, itemType, itemName):
print "Creating item %s with name %s" % (itemType, itemName)
def create_wrapper(self, name):
self.create(self.createItem, name)
def __getattr__(self, attrName):
self.createItem = attrName
return self.create_wrapper
This will work when the user calls something like:
a = c.A("nameA")
b = c.B("nameB")
However, it will fall over in situations where the function pointers are stored without being called:
aFunc = c.A
bFunc = c.B
aFunc("nameA") # Is actually calling c.create("B", "nameA"),
# as c.B was the last __getattr__() call
bFunc("nameB")
Any suggestions for anything I'm missing here?
Thanks
Edit: I appear to have just figured this one out, but Philipp has a far more elegant solution....
My solution was:
class MyClassCreator():
def __init__(self, origClass, itemType):
self.origClass = origClass
self.itemType = itemType
def create_wrapper(self, name):
return self.origClass.create(self.itemType, name)
class MyClass():
def __init__(self):
self.createItem = ""
def create(self, itemType, itemName):
print "Creating item %s with name %s" % (itemType, itemName)
def __getattr__(self, attrName):
return MyClassCreator(self, attrName).create_wrapper
The version that I actually ended up using (as I needed more complexity than a single argument) is: (I don't know if this can be done using a lambda function...)
def __getattr__(self, attrName):
def find_entity_wrapper(*args, **kwargs):
return self.find_entity(attrName, *args, **kwargs)
return find_entity_wrapper
Have __getattr__
return a local wrapper function:
class MyClass(object):
def create(self, itemType, itemName):
print "Creating item %s with name %s" % (itemType, itemName)
def __getattr__(self, attrName):
def create_wrapper(name):
self.create(attrName, name)
return create_wrapper
There are other ways to create the wrapper function. The simplest one in this case is to use functools.partial
:
import functools
class MyClass(object):
def create(self, itemType, itemName, *args, **kwargs):
print "Creating item %s with name %s, args %r and kwargs %r" % (itemType, itemName, args, kwargs)
def __getattr__(self, attrName):
return functools.partial(self.create, attrName)
c = MyClass()
bFunc = c.B
bFunc("nameB", 1, 2, foo=3)
This will automatically pass all remaining args to the wrapped function.
You can get what you want by simplifying:
class MyClass():
def create(self, itemType, itemName):
print "Creating item %s with name %s" % (itemType, itemName)
def __getattr__(self, attrName):
return lambda x: self.create(attrName, x)
c = MyClass()
a = c.A("nameA")
b = c.B("nameB")
af = c.A
bf = c.B
af("nameA")
bf("nameB")
prints:
Creating item A with name nameA
Creating item B with name nameB
Creating item A with name nameA
Creating item B with name nameB
精彩评论