用循环覆盖类的几个方法

时间:2016-05-30 10:13:37

标签: python class methods

我想知道是否有一种简单的方法可以对类的几种方法进行相同的编辑。一个例子:

class Dog():
    def __init__(self):
        self.name = 'abc'
        self.age = 1

    def setName(self, newValue):
        self.name = newValue

    def setAge(self, newValue):
        self.age = newValue

class TalkingDog(Dog):

    def __init__(self):
        super().__init__()
        # The end is in pseudo code : 
        for method in TalkingDog.allMethods :
            method = method + 'print('I have been edited !')'

我知道我也可以覆盖每种方法,但在有数十种方法的情况下,这会有点无聊......

所以我尝试了这个:

class TalkingDog(Dog):

    def __init__(self):
        super().__init__()
        for method in self.__dir__():
            if method.startswith('set'):
                oldMethod = getattr(self, method)
                def _newMethod(newValue):
                    oldMethod(newValue)
                    print('I have been edited !')
                setattr(self, method, _newMethod)


a = TalkingDog()
print(a.setName) >>> <function TalkingDog.__init__.<locals>._newMethod at 0x0000000002C350D0>

这几乎可以,但是setName不再是一种方法。它是一个包含函数的属性。我完全理解为什么,但我想要获得更清晰的结果。结果,我有可能在以后遇到问题。例如,我不能使用带有该对象的库pickle(得到错误_pickle.PicklingError: Can't pickle <function TalkingDog.__init__.<locals>._newMethod at 0x00000000003DCBF8>: attribute lookup _newMethod on __main__ failed)。

2 个答案:

答案 0 :(得分:2)

Pythonic的方法可能是使用descriptor protocol,这也是属性使用的:

class VocalAttribute:

    def __init__(self, name, feedback):
        """Called when you first create the descriptor."""
        self.name = name  # the name of the attribute 'behind' the property
        self.feedback = feedback  # the feedback to show when the value changes

    def __get__(self, obj):
        """Called when you get the descriptor value."""
        return getattr(obj, self.name)

    def __set__(self, obj, value):
        """Called when you set the descriptor value."""
        prev = getattr(obj, self.name, None)
        if value != prev:
            setattr(obj, self.name, value)
            print(self.feedback)

    def __delete__(self, obj):
        """Called when you delete the descriptor value."""
        delattr(obj, self.name)


class Foo:

    bar = VocalAttribute('_bar', 'I have been edited!')


foo = Foo()

print('1.')

foo.bar = 'hello'

print('2.')

foo.bar = 'hello'

print('3.')

foo.bar = 'world'

输出:

1.
I have been edited!
2.
3.
I have been edited!

请注意,这只会在新值与旧值不同时提供反馈 - 您可以根据需要调整__set__中的行为。这也意味着你可以直接读取并分配给foo.bar,而不需要调用getter和setter(这是什么,Java?)

答案 1 :(得分:-1)

因为decorator可以明确地在这里调用一种方法来使用它:

def updater(obj, call_back, call_back_args=(), call_back_kw=None, replace=False):
    # ability to be called on the fly with different args and kw for the callback
    # now it returns the updated obj (instance or class) 
    # but could a be factory returning a new obj in this case make a copy of obj, update this coy and return it

    def update_function(fn, *args, **kw):
        def wrapper(*args, **kw):
            if replace:
                # call only the callback
                res = call_back(*call_back_args, **call_back_kw)
            else:
                res = fn(*args, **kw)
                call_back(*call_back_args, **call_back_kw)
            return res
        return wrapper

    # get all methods of the obj
    # and apply update_function (a decorator) to all methods
    for name, m in inspect.getmembers(
            obj, predicate=lambda x: inspect.isfunction(x) or inspect.ismethod(x)):
        # make the selection here
        # could be made on the name for instance
        if not name.startswith('_'):
            new_m = update_function(m)
            setattr(obj, name, new_m)

    return obj

# declare a callback 
def call_back(*args, **kw):
    # simple callback
    print("I have been edited and called with %r args and %r kw " % (args, kw))

a = Dog()

# could be called on instance or class
# apply the callback on all "public" methods 
updater(
    a,
    call_back,
    call_back_args=(2, 3, 4),
    call_back_kw={"kw1": "v_1"}
)