Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/python-2.7/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在python中将用户选择的方法添加到元基类_Python_Python 2.7_Methods_User Input_Metaclass - Fatal编程技术网

在python中将用户选择的方法添加到元基类

在python中将用户选择的方法添加到元基类,python,python-2.7,methods,user-input,metaclass,Python,Python 2.7,Methods,User Input,Metaclass,我有一个不能实例化的基类(BaseFruit,在我的简化示例中)和一些派生类(例如Apple,在我的示例中),它们都应该共享一个相同的方法(printfourture)。但是,它们有很多可能的变体,我想让用户选择应该使用的变体(例如sadfourture和saddestfuture) 由于我的printfourture方法对于我的所有派生类来说都是通用的,因此我认为应该使用基类的\uuuu new\uuuu方法捕获变量的用户参数,并将该方法分配给基类本身。如下例所示: # my custom m

我有一个不能实例化的基类(
BaseFruit
,在我的简化示例中)和一些派生类(例如
Apple
,在我的示例中),它们都应该共享一个相同的方法(
printfourture
)。但是,它们有很多可能的变体,我想让用户选择应该使用的变体(例如
sadfourture
saddestfuture

由于我的
printfourture
方法对于我的所有派生类来说都是通用的,因此我认为应该使用基类的
\uuuu new\uuuu
方法捕获变量的用户参数,并将该方法分配给基类本身。如下例所示:

# my custom methods

def sadfuture(self):
    """A sad future"""
    print 'It looks {}!'.format(self.aspect)
    print 'Mmm yummy !'

def saddestfuture(self):
    """A saddest future"""
    print 'It looks {}'.format(self.aspect)
    print 'Garbage !'

# my base class

class BaseFruit(object):
    """Fruit base class"""
    def __new__(cls, *args, **kwargs):
        setattr(cls, 'printfuture', kwargs['usermethod'])
        return object.__new__(cls)

# My class

class Apple(BaseFruit):
   """An apple class"""
   def __init__(self, aspect, usermethod=sadfuture):
        self.aspect = aspect


if __name__ == '__main__':
    goodapple = Apple(aspect='delicious', usermethod=sadfuture)
    goodapple.printfuture() # ==> ok
    badapple = Apple(aspect='rotten', usermethod=saddestfuture)
    goodapple.printfuture() # ==> not ok anymore
    badapple.printfuture() # ==> ok
其中打印:

It looks delicious!
Mmm yummy !
It looks delicious
Garbage !
It looks rotten
Garbage !
而不是预期的行为:

It looks delicious!
Mmm yummy !
It looks delicious!
Mmm yummy !
It looks rotten
Garbage !
我知道我已经覆盖了我的基类,我的第一个对象已经改变了它的行为。因此,我的主要问题是:如何实现预期行为,同时将自定义方法排除在基类之外?


还欢迎就最佳做法和针对此类问题的适当设计发表意见

我认为问题来自于底层阶级

当您使用BaseFruit并将其用作Apple类的基类时,python会将Apple类和BaseFruit类中存在的任何值直接分配给BaseFruit类。由于两个“苹果”都基于相同的基类,因此它们共享来自该类的值

当您将
saddestfuture
设置为要执行的函数时,您会根据BaseFruit类为所有对象设置一种“全局”函数

你需要一条线

self.userm = usermethod
在苹果的的初始化中。然后将这个
self.userm
作为
kwarg
传递给基类,而不是字符串
“usermethod”

我不清楚这个操作的语法,因为我已经很长时间没有使用python继承规则了,我承认我已经忘记了语法。也许其他人可以在评论中提出代码,或者你自己发现:-)。

预期的行为实际上是打印出来的。所以,这种行为不是“你所期待的”,这是另一回事。让我们看看为什么:

每次创建一个新的Apple实例时,您要做的就是在实例化的类上创建一个新的方法(在本例中是,
Apple
)。每次创建
BaseFruit
或其任何子类的新实例时,
setattr(cls,'printfourture',kwargs['usermethod'])行就是这样做的。(顺便说一下,这一行可以是
cls.printfourture=kwargs['usermethod']
,如果属性名是硬编码的,则不需要
setattr

因此,当您创建
Apple
的第二个实例时,调用
badapple=Apple(aspect='root',usermethod=saddestfuture)
只需将
saddestfuture
printfuture
类设置为
saddestfuture
,而不仅仅是
badapple
的方法,但是对于苹果公司来说

修复不需要元类的问题-您可以使用
\uuuuu new\uuuuu
本身中的代码创建一个“伪方法”,改为附加到实例-如您所愿。但是在实例创建之后,当您有一个对实例的引用时,而不是在实例化之前,当您只有一个对类本身的引用时,您必须在实例上这样做

由于在安装类之前没有实际需要运行的代码,您最好在
\uuuuu init\uuuuu
中绑定类似方法的函数,并将自定义
\uuu new\uuuuuu
保留在真正需要的时候。在这种情况下,使用
super
而不是硬编码超类的调用不会有什么坏处:

...
# my base class

class BaseFruit(object):
    """Fruit base class"""
    def __init__(self, *args, **kwargs):
        printfuture = kwargs.pop('usermethod')
        super(BaseFruit, self).__init__(*args, **kwargs)
        # Wrap the call to the passed method in a function
        # that captures "self" from here, since Python do not 
        # insert "self" in calls to functions 
        # attributed to instances.
        self.printfuture = lambda: printfuture(self)


# My class

class Apple(BaseFruit):
   """An apple class"""
   def __init__(self, aspect, usermethod=sadfuture):
        super(Apple, self).__init__(usermethod)
        self.aspect = aspect
至于元类,这并不需要它们——相反,您必须在创建每个实例时对其进行自定义。当我们必须自定义类本身时,我们通常使用元类。您的原始代码正在这样做(自定义类本身),但该类的代码在创建每个实例时运行,这导致了您不期望的行为。如果创建
printfourture
方法的代码在元类
\uuuuu new\uuuuu
方法上,与在超类中不同的是,当声明每个子类时,只会发生一次(并且该子类的所有实例将共享相同的
printifuture
方法)


现在,一旦你掌握了它是如何工作的,请转到Python3继续学习这些东西。Python2将在2年后完全结束,在任何项目中都是无用的。一件事是必须在Python2中保留遗留代码,另一件事是学习或启动新项目-您应该只使用Python3来实现这一点。

请指定预期的行为是什么?@monamona done!我强烈怀疑问题是什么,但键入答案需要一段时间。不要选择方法;为每个方法定义一个子类,并让用户选择子类。或者,简单地围绕可能的方法定义一个字典,让“real”方法简单地从
dict
调用用户选择的函数。Wy您是否正在研究类和对象创建的高级方面,并使用Python2.x来实现这一点,Python2.x已经过时8年多了?