动态方法绑定与继承在Python [英] Dynamic method binding with inheritance in Python

查看:281
本文介绍了动态方法绑定与继承在Python的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很喜欢python(〜一个月),我希望我早点转过身去(经过多年的perl)。



问题:我希望对象具有与其类型相同的方法调用的不同功能。方法是在运行时分配的,基于加载的模块(组成所有对象)。



问题:我想知道是否有一个流行的设计模式,我可以使用而不是下面,或者如果这已经有一个设计模式名称(我很遗憾没有正式的CS背景,并知道这将有助于我的文档)



我有一个类层次结构(现在,其中26个具有3个基类)。只有基类有一些简单的方法(例如:add_child),每个派生类只能使用新的数据属性(特定于派生类)扩展基类,必要时覆盖方法(例如:__str __)。



我正在处理节点不同类的树。然而,节点应该具有相同的某些方法名称( stringify print_tree code>, eval transform_X 等),从而允许轻松/盲人的迭代器操作。每个方法都可以做一些不同的方法,但这些方法具有相同的调用名称(如多态性)。



我主要想授予特定的能力方法)到节点,根据它们的类型。最初,我使用访客模式实现了这一点。但是,然后意识到我没有真正需要考虑到我在Python中工作。



在下面的例子中,我有动态分配给类的方法。请注意,在下面的示例中,迭代/递归方法调用名称( print_tree )与函数名称不同( generic__print_tree ) 。

 #astmethods.py 
def generic__print_tree(self,level = 1)

描述:具有缩进的打印节点
目标:任何树节点

print({}> {}format ' - '* level,self))
for self.children中的child:
child.print_tree((level + 1))


def ASTNode__stringify(self )

描述:返回此节点下的树的字符串表示
目标:AST / CFG节点

text = str(self )
for self.children中的child:
text + =,{+ child.stringify()+}
返回文本

最后,主要模块具有这个功能,在模块中被调用的扩展类型()它。预计这些节点将在此模块的上下文中根据其类型(不是值)执行不同的事情。分配的方法是继承的,除非被覆盖。

 #mainModule1.py 
def extend_types ():

使用整齐的函数升级AST节点类,以便在此模块的上下文中使用

#跨层次结构的简单函数
#我应该从一个普通基类派生它们,以避免这个
ASTNode.print_tree = generic__print_tree
SimpleNode.print_tree = generic__print_tree

#ASTNode和所有派生类获取此方法
ASTNode.stringify = ASTNode__stringify

#所有AST节点获取基本方法,除了Ignore和Arraysel类型节点
#遍历与child.tidy()$ b相同$ b ASTNode.tidy = ASTNode__tidy
ASTIgnore.tidy = ASTIgnore__tidy
ASTArraySel.tidy = ASTArraySel__tidy

#所有AST节点获得基本方法,但覆盖范围除外,如果类型为no des
ASTNode.transform_controlFlow = ASTNode__transform_controlFlow
ASTCoverage.transform_controlFlow = ASTCoverage__transform_controlFlow
ASTIf.transform_controlFlow = ASTIf__transform_controlFlow

编辑:删除分散信息,作为单个模块上下文的示例

解决方案

问题摘要< h2>

忽略不相关的细节,这里的问题可以总结如下:



有一个基类和许多派生类。有一些功能应该适用于所有派生类,但是依赖于一些外部开关(在 main module 选项)中。 p>

问题的想法是根据交换机对基类进行monkeypatch。



解决方案



而不是这样,取决于外部开关的功能应该分开。 >

例如:

 #有一个基础类:

class ASTNode(object):
pass


#有很多派生类,例如:


class ASTVar (ASTNode):
pass


#应该为所有对象定义一个函数stringify或另一个
#的一个实现,具体取决于某些外部因素


def stringify1(node):
#something


def stringify2(node):
#something else


#根据不同,选择其中之一:

stringify = stringify1

现在使用的内容与原始代码有点不同: node.stringify()的现在有$ code>字符串化(节点)。但是没有任何错误。



BTW ...



也许会更令人愉快的使用一个类:



class NodeHandler1(object):
def print_tree(node):
#do something

  def stringify(node):
#做某事

...

但是根本不需要。



道德



不要monkeypatch。这总是糟糕的设计。


I am new to python (~ a month), and I wish I had switched to it sooner (after years of perl).

Problem: I want objects to have different functionality to the same method call based on their type. The methods are assigned at runtime, based on the module loaded (which composes all objects).

Question: I wanted to know if there was a popular design pattern that I could use instead of the below, or if this already has a design pattern name (I sadly have no formal CS background, and knowing this will help my documentation)?

I have a class hierarchy (as of now, 26 of them with 3 base classes). Only the base classes have some trivial methods (eg: add_child), and each derived class only extends the base class with new data attributes (specific to the derived class), overriding methods when necessary (eg: __str__).

I am dealing with tree(s) where nodes are of different classes. Yet, the nodes should the same certain method names (stringify, print_tree, emit, eval, transform_X, etc), thereby allowing easy/blind iterator operation. Each method may do something different, yet the methods have the same call name (like polymorphism).

I primarily wanted to grant specific abilities (methods) to nodes, based on their type. Initially, I implemented this using the Visitor Pattern. But, then realized I didn't really have to, considering I was working in Python.

In the below example, I have methods which are dynamically assigned to classes. Note, in below example the iteration/recursion method call name (print_tree) is different from the function name (generic__print_tree).

#astmethods.py
def generic__print_tree(self, level=1):
    """
    Desc: print nodes with indentation
    Target: Any tree node
    """
    print("{}> {}".format('-' * level, self))
    for child in self.children:
            child.print_tree((level + 1))


def ASTNode__stringify(self):
    """
    Desc: Return string representation of the tree under this node
    Target: AST/CFG nodes
    """
    text = str(self)
    for child in self.children:
            text += ", { " + child.stringify() + " }"
    return text

Finally the main modules has this function, extend_types() which gets called during module init. The nodes are expected to do different things, within the context of this module, based on their type (not value). The methods assigned are inherited, unless overridden.

# mainModule1.py
def extend_types():
    """
    Upgrade the AST node classes with neat functions for use within this module's context
    """
    # same simple functions across class hierarchies
    # I should just derive them both from a common base class to avoid this
    ASTNode.print_tree = generic__print_tree
    SimpleNode.print_tree = generic__print_tree

    # ASTNode and all derived class get this method
    ASTNode.stringify = ASTNode__stringify

    # All AST nodes get the base method, except for Ignore and Arraysel type nodes
    # traversal looks the same with child.tidy()
    ASTNode.tidy = ASTNode__tidy
    ASTIgnore.tidy = ASTIgnore__tidy
    ASTArraySel.tidy = ASTArraySel__tidy

    # All AST nodes get the base method, except for the Coverage and If type nodes
    ASTNode.transform_controlFlow = ASTNode__transform_controlFlow
    ASTCoverage.transform_controlFlow = ASTCoverage__transform_controlFlow
    ASTIf.transform_controlFlow = ASTIf__transform_controlFlow

edit: removed distracting info, made the example for a single module context

解决方案

Problem Summary

Ignoring the irrelevant details, the problems here can be summed up as follows:

There is one base class and many derived classes. There is a certain functionality which should apply to all of the derived classes, but depends on some external switch (in the question: choice of "main module").

The idea in the question is to monkeypatch the base class depending on the switch.

Solution

Instead of that, the functionality which depends on the external switch should be separated.

In example:

# There is a base class:

class ASTNode(object):
    pass


# There are many derived classes, e.g.:


class ASTVar(ASTNode):
    pass


# One implementation of function stringify or another
# should be defined for all objects, depending on some external factor


def stringify1(node):
    # something


def stringify2(node):
    # something else


# Depending on whatever, choose one of them:

stringify = stringify1

This is now used just a little bit differently than in the original code: intead of node.stringify(), there is now stringify(node). But there is nothing wrong with that.

BTW...

Maybe it would be more pleasing to the eye to use a class:

class NodeHandler1(object): def print_tree(node): # do something

def stringify(node):
    # do something

...

But that is not required at all.

The Moral

Do not monkeypatch. That is always bad design.

这篇关于动态方法绑定与继承在Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆