Python:允许未特定定义的方法像__getattr__一样被调用

5 投票
2 回答
1820 浏览
提问于 2025-04-16 02:30

我正在尝试写一个Python类,想要实现以下功能:

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和B可以是任何东西(其实它们是在数据库中定义的,但我不想在代码里明确写出来)

一个比较简单的解决办法是这样做:

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

当用户调用类似这样的内容时,它会正常工作:

a = c.A("nameA")
b = c.B("nameB")

不过,在函数指针被存储但没有被调用的情况下,它就会出问题:

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

有没有什么建议,能帮我补充一下我遗漏的部分?

谢谢

补充:我好像刚刚弄明白了这个问题,但Philipp有一个更优雅的解决方案……

我的解决方案是:

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

我最后用的版本(因为我需要比单个参数更复杂的东西)是:(我不知道这是否可以用lambda函数来实现……)

def __getattr__(self, attrName):
    def find_entity_wrapper(*args, **kwargs):
        return self.find_entity(attrName, *args, **kwargs)

    return find_entity_wrapper

2 个回答

6

你可以通过简化来获得你想要的结果:

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

输出结果是:

Creating item A with name nameA
Creating item B with name nameB
Creating item A with name nameA
Creating item B with name nameB
8

__getattr__ 返回一个本地的包装函数:

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

还有其他方法可以创建这个包装函数。在这种情况下,最简单的方法是使用 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)

这样做会自动把所有剩下的参数传递给被包装的函数。

撰写回答