Python 27:如何修复/存储方法的值以保留其接口? [英] Python 27: How to fix/store a value to a method to preserve its interface?

查看:35
本文介绍了Python 27:如何修复/存储方法的值以保留其接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果python中的一切都是对象,那么为什么以下不起作用?

class Hello(object):定义你好(自己):打印(x)h = 你好()h.hello.x = "你好世界"

这样做时我得到:

AttributeError: 'instancemethod' 对象没有属性 'value'

我可以通过使用 partial 来实现这一点,但是我不确定这会对对象产生什么影响.还有其他方法可以实现吗?

from functools import partial类你好(对象):def hello(self, x):打印(x)h = 你好()newMethod = partial(h.hello, "helloworld")新方法()

好的,从下面的评论中,人们想要一个真实"的场景一个例子,可以被认为是以下:

file.txt 包含以下列表[ItemID1"、ItemID2"、ItemID3"、ItemID4"]def BindMethods(self):# ["ItemID1", "ItemID2", "ItemID3", "ItemID4"]items = readFromFile("file.txt")self.bind(AN_EVENT, GenericMethod)def GenericMethod(self, i, event):#调用这个方法的人知道这个方法# 只接受一个参数,而不是两个.# 我试图保留我无法控制的界面数据 = generateDataFromID(i)表[i] = 数据

这里的想法是将一个方法绑定到一个事件,从而调用者期望具有特定签名的方法,即单个输入参数.

但是在绑定时,您希望将一些额外的信息传递给处理程序.您不知道要传递多少信息,因为它是可变的.

同一事物的扩展/编译时版本将是这样的:

file.txt 包含以下列表[ItemID1"、ItemID2"、ItemID3"、ItemID4"]def BindMethods(self):# ["ItemID1", "ItemID2", "ItemID3", "ItemID4"]items = readFromFile("file.txt")self.bind(AN_EVENT, self.Item1Method)self.bind(AN_EVENT, self.Item2Method)self.bind(AN_EVENT, self.Item3Method)......def Item1Method(self, event):数据 = generateDataFromID("Item1")table["Item1ID"] = 数据def Item2Method(self, event):数据 = generateDataFromID("Item2")table["Item2ID"] = 数据

解决方案

在您编辑之后,在我看来,这似乎是您寻求将某些事件路由到某些功能的可能性,对吗?>

与其将新属性绑定到现有的实例方法,不如考虑建立一个干净且可扩展的事件路由系统.

一个基本的路由核心

虽然通常应用 singleton 模式总是一个很好的方式来问问自己是否遵循了正确的概念,但在这里完全有道理.路由的核心类似于以下内容:

def 单例(cls):实例 = cls()instance.__call__ = lambda: 实例返回实例@单身人士类事件路由器(对象):def __init__(self):self._routes = dict()def 绑定(self, key, func):self._routes[key] = func定义路线(自我,关键):如果键入 self._routes.keys():返回 self._routes[key]()别的:raise KeyError('未绑定路由密钥:{}'.format(key))

EventRouter 是一个单例类,可以将函数绑定到某些键,并且还能够将发生的事件路由到所需的目标函数.
您现在可以在运行时将事件绑定到所需的函数:

def my_event_function():print('一个事件被处理了.')EventRouter.bind('E_MYEVT', my_event_function)# 如果您正在迭代事件(例如从文件中提取),# 您可以将它们定向到它们的功能:对于 ['E_MYEVT', 'E_WHATEVT', 'E_NOEVT'] 中的 e:EventRouter.route(e)

路由装饰器

如果需要,您可以设置另一个装饰器,用于将方法直接绑定到某个事件类型:

def eventroute(key):def register_method(call):EventRouter.bind(key, call)返回 register_method@eventroute('E_MYEVT')def handle_myevt():print('处理事件:E_MYEVT')@eventroute('E_WHATEVT')def handle_whatevt():print('处理事件:E_WHATEVT')如果 __name__ == '__main__':对于 ['E_MYEVT', 'E_WHATEVT'] 中的 e:EventRouter.route(e)>>>处理事件:E_MYEVT>>>处理事件:E_WHATEVT

将参数传递给路由端点

如果你想向端点传递参数,只需在转发调用时通过可选的关键字参数**kwargs扩展调用:

EventRouter 类中,将route 方法更改为:

def route(self, key, **kwargs):如果键入 self._routes.keys():返回 self._routes[key](**kwargs)别的:raise KeyError('未绑定路由密钥:{}'.format(key))

对于您的端点,设置所需的参数.在这个例子中,我使用参数 filename:

@eventroute(...)def handle_...(文件名):print('处理事件:...')打印(文件名)

并且在调度事件时,以 kwarg 的形式提供所需的参数:

events = ['E_MYEVT', 'E_WHATEVT']对于事件中的 e:EventRouter.route(e, filename='whatever.jpg')>>>处理事件:E_MYEVT>>>随便.jpg>>>处理事件:E_WHATEVT>>>随便.jpg

去哪里

这只是一个非常基本的设置的简要概述.
您可以将 EventRouter 扩展为任意复杂,例如在多层层次结构中添加路由键等.此外,您可以向方法添加更多参数.你甚至可以考虑扩展 eventroute 装饰器来定义 kwargs,它应该传递给端点函数,以保持函数签名尽可能通用.

可能看起来有点矫枉过正,但像这样的结构非常简洁,任何导入您的库/脚本并希望为单个事件添加自己的端点的人都可以扩展.

If everything is an object in python then why does the following not work?

class Hello(object):

    def hello(self):
        print(x)

h = Hello()
h.hello.x = "hello world"

When doing this I get:

AttributeError: 'instancemethod' object has no attribute 'value'

A way which I can achieve this is by using partial however I am not sure what the effects of that would be on an object. Is there another way to achieve this?

from functools import partial
class Hello(object):

    def hello(self, x):
        print(x)

h = Hello()
newMethod = partial(h.hello, "helloworld")
newMethod()

Okay, from the comments below, people want a "real" scenario one example which can be considered as the following:

file.txt contains the following list
["ItemID1", "ItemID2", "ItemID3", "ItemID4"]

def BindMethods(self):
    # ["ItemID1", "ItemID2", "ItemID3", "ItemID4"]
    items = readFromFile("file.txt")
    self.bind(AN_EVENT, GenericMethod)

 def GenericMethod(self, i, event):
     # The person calling this method knows that the method
     # only accepts a single argument, not two.
     # I am attempting to preserve the interface which I do not have control over
     data = generateDataFromID(i)
     table[i] = data

The idea here being that you bind a method to an event whereby the caller is expecting the method of a specific signature i.e. a single input argument.

But at the point of binding you want to pass in some extra information down to the handler. You don't know how much information you want to pass down since it is variable.

A expanded/compile time version of the same thing would be something like this:

file.txt contains the following list
["ItemID1", "ItemID2", "ItemID3", "ItemID4"]

def BindMethods(self):
    # ["ItemID1", "ItemID2", "ItemID3", "ItemID4"]
    items = readFromFile("file.txt")
    self.bind(AN_EVENT, self.Item1Method)
    self.bind(AN_EVENT, self.Item2Method)
    self.bind(AN_EVENT, self.Item3Method)
    ......

 def Item1Method(self, event):
     data = generateDataFromID("Item1")
     table["Item1ID"] = data

 def Item2Method(self, event):
     data = generateDataFromID("Item2")
     table["Item2ID"] = data

解决方案

After your edit, this seems to me as if you seek a possibility to route certain events to certain functions, right?

Instead of binding new attributes to existing instance methods, you should think about setting up a clean and extendable event routing system.

A Basic Routing Core

While usually application of the singleton pattern always is a good point to ask oneself whether one is following the right concept, it totally makes sense here. The core of your routing is something similar to this:

def singleton(cls):
    instance = cls()
    instance.__call__ = lambda: instance
    return instance

@singleton
class EventRouter(object):
    def __init__(self):
        self._routes = dict()

    def bind(self, key, func):
        self._routes[key] = func

    def route(self, key):
        if key in self._routes.keys():
            return self._routes[key]()
        else:
            raise KeyError('Unbound routing key: {}'.format(key))

The EventRouter is a singleton class that can bind functions to certain keys and is furthermore able to route occurring events to their desired destination functions.
You now kan bind an event to a desired function during runtime:

def my_event_function():
    print('An event was handled.')

EventRouter.bind('E_MYEVT', my_event_function)

# If you are iterating over events (for example drawn from a file),
# you can direct them to their functions:
for e in ['E_MYEVT', 'E_WHATEVT', 'E_NOEVT']:
    EventRouter.route(e)

A Routing Decorator

If required, you can set up another decorator that can be used to directly bind methods to a certain event type:

def eventroute(key):
    def register_method(call):
        EventRouter.bind(key, call)
    return register_method

@eventroute('E_MYEVT')
def handle_myevt():
    print('Handling event: E_MYEVT')

@eventroute('E_WHATEVT')
def handle_whatevt():
    print('Handling event: E_WHATEVT')

if __name__ == '__main__':
    for e in ['E_MYEVT', 'E_WHATEVT']:
        EventRouter.route(e)

>>> Handling event: E_MYEVT
>>> Handling event: E_WHATEVT

Passing Arguments to the Route Endpoints

If you want to pass arguments to the endpoints, just extend the calls by optional keyword arguments **kwargs when forwarding the call:

In EventRouter class, change route method to:

def route(self, key, **kwargs):
    if key in self._routes.keys():
        return self._routes[key](**kwargs)
    else:
        raise KeyError('Unbound routing key: {}'.format(key))

For your endpoints, set the desired arguments. In this example, I use the argument filename:

@eventroute(...)
def handle_...(filename):
    print('Handling event: ...')
    print(filename)

And when dispatching your events, provide the required argument as kwarg:

events = ['E_MYEVT', 'E_WHATEVT']
for e in events:
    EventRouter.route(e, filename='whatever.jpg')

>>> Handling event: E_MYEVT
>>> whatever.jpg
>>> Handling event: E_WHATEVT
>>> whatever.jpg

Where to go

This is a brief overview of just a very basic setup.
You can extend your EventRouter to be arbitrarily complex, e.g. add routing keys in a multi-layered hierarchy, or the like. Furthermore, you can add more arguments to your methods. You can even think about extending the eventroute decorator to define the kwargs that shall be passed to the endpoint function to keep the function signatures as generic as possible.

Might seem like overkill, but structures like these are pretty neat and can be extended by anyone who imports your library/script and wants to add her own endpoints for individual events.

这篇关于Python 27:如何修复/存储方法的值以保留其接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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