Context dependent method in Python - what am I doing wrong?

廉价感情. 提交于 2019-12-06 15:53:59

问题


I would like to create a class whose f method depends on the "mode" the object of the class has been created.

The code below doesn't work but hope it gets you an idea of what I am trying to do. My idea is to have a dictionary in which I define the settings for each mode (in this case the function or method to assign to self.f, so that rather than using many if elif statements in the init function I just assign the correct values using the dictionary.

class A(object):
    _methods_dict={
            'a':A.f1,
            'b':A.f2
            }    

    def __init__(self,mode = 'a'):
        self.f = _methods_dict[mode]


    def f1(self,x):
        return x

    def f2(self,x):
        return x**2

I can't figure why this does not work, how would you fix it? Also are there better (and more pythonic) approaches to get the same kind of functionalities?


回答1:


Store the name of the two functions, then use getattr() to retrieve the bound method in __init__:

class A(object):
    _methods_dict = {
        'a': 'f1',
        'b': 'f2'
    }    

    def __init__(self, mode='a'):
        self.f = getattr(self, self._methods_dict[mode])

    def f1(self, x):
        return x

    def f2(self, x):
        return x ** 2

Alternatively, just proxy the method:

class A(object):
    _methods_dict = {
        'a': 'f1',
        'b': 'f2'
    }

    def __init__(self,mode = 'a'):
        self._mode = mode

    @property
    def f(self):
        return getattr(self, self._methods_dict[self._mode])

    def f1(self, x):
        return x

    def f2(self, x):
        return x ** 2

The f property just returns the correct bound method for the current mode. Using a property simplifies call signature handling, and gives users the actual method to introspect if they so wish.

Either method has the same end-result:

>>> a1 = A()
>>> a2 = A('b')
>>> a1.f(10)
10
>>> a2.f(10)
100

The difference lies in what is stored in the instance, the first method stores bound methods:

>>> vars(a1)
{'f': <bound method A.f1 of <__main__.A object at 0x10aa1ec50>>}
>>> vars(a2)
{'f': <bound method A.f2 of <__main__.A object at 0x10aa1ed50>>}

versus the method in the other:

>>> vars(a1)
{'_mode': 'a'}
>>> vars(a2)
{'_mode': 'b'}

That may not seem much of a difference, but the latter method creates instances that can be pickled and deep-copied without problems.




回答2:


You could just make two separate classes:

class Base(object):
    # place here all attributes shared in common among the Modes
    pass

class ModeA(Base):
    def f(self, x):
        return x

class ModeB(Base):
    def f(self, x):
        return x**2

def make_mode(mode, *args, **kwargs):
    mode_dict = {'a':ModeA, 'b':ModeB}
    return mode_dict[mode](*args, **kwargs)

a = make_mode('a')
print(a.f(10))
# 10

b = make_mode('b')
print(b.f(10))
# 100



回答3:


To answer your first question ("why this does not work"): the class object "A" is only created and bound to the module name "A" after the whole class statement (yes, "class" is an executable statement) block has ended, so you cannot refer to neither the name or the class object itself within this block.



来源:https://stackoverflow.com/questions/15944312/context-dependent-method-in-python-what-am-i-doing-wrong

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