Python : Allowing methods not specifically defined to be called ala __getattr__

萝らか妹 提交于 2019-12-06 03:33:09

问题


I'm trying to write a Python class that has the ability to do the following:

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

回答1:


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.




回答2:


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


来源:https://stackoverflow.com/questions/3434938/python-allowing-methods-not-specifically-defined-to-be-called-ala-getattr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!