前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python技巧 - 函数、方法的动态调用

python技巧 - 函数、方法的动态调用

原创
作者头像
MicLon
发布2023-02-27 20:16:06
9160
发布2023-02-27 20:16:06
举报
文章被收录于专栏:python-进阶python-进阶

今天逛github的时候看到这样一个项目,其中在RPC远程调用接口中实现一个功能,并用add_method进行装饰,于是我把它从项目中摘出来。

并在此基础上,我额外增加了add_missing_method方法,用于包装一个自定义方法,处理拦截未找到方法的情况。

以下代码演示了如何动态调用函数、方法。

代码语言:python
复制
# -*- coding: utf-8 -*-
import functools
try:
    from collections.abc import MutableMapping
except ImportError:
    from collections import MutableMapping
class Dispatcher(MutableMapping):
    """ Dictionary like object which maps method_name to method."""
    def __init__(self, prototype=None):
        """ Build method dispatcher.
        Parameters
        ----------
        prototype : object or dict, optional
            Initial method mapping.
        Examples
        --------
        Init object with method dictionary.
        >>> Dispatcher({"sum": lambda a, b: a + b})
        None
        """
        self.missing_method_name = '__missing__'
        self.method_map = dict()
        if prototype is not None:
            self.build_method_map(prototype)
    def __getitem__(self, key):
        if key in self.method_map:
            return self.method_map[key]
        if self.missing_method_name in self.method_map:
            return self.method_map[self.missing_method_name]
        raise Exception('missing method <' + key + '> if you want to close the Exception, you can use '
                                                   'add_missing_method.')
    def __setitem__(self, key, value):
        self.method_map[key] = value
    def __delitem__(self, key):
        del self.method_map[key]
    def __len__(self):
        return len(self.method_map)
    def __iter__(self):
        return iter(self.method_map)
    def __repr__(self):
        return repr(self.method_map)
    def add_class(self, cls):
        prefix = cls.__name__.lower() + '.'
        self.build_method_map(cls(), prefix)
    def add_object(self, obj):
        prefix = obj.__class__.__name__.lower() + '.'
        self.build_method_map(obj, prefix)
    def add_dict(self, dict, prefix=''):
        if prefix:
            prefix += '.'
        self.build_method_map(dict, prefix)
    def add_method(self, f=None, name=None):
        """ Add a method to the dispatcher.
        Parameters
        ----------
        f : callable
            Callable to be added.
        name : str, optional
            Name to register (the default is function **f** name)
        Notes
        -----
        When used as a decorator keeps callable object unmodified.
        Examples
        --------
        Use as method
        >>> d = Dispatcher()
        >>> d.add_method(lambda a, b: a + b, name="sum")
        <function __main__.<lambda>>
        Or use as decorator
        >>> d = Dispatcher()
        >>> @d.add_method
            def mymethod(*args, **kwargs):
                print(args, kwargs)
        Or use as a decorator with a different function name
        >>> d = Dispatcher()
        >>> @d.add_method(name="my.method")
            def mymethod(*args, **kwargs):
                print(args, kwargs)
        """
        if name and not f:
            return functools.partial(self.add_method, name=name)
        self.method_map[name or f.__name__] = f
        return f
    def add_missing_method(self, f=None, name=None):
        """ Add missing method to the dispatcher"""
        self.missing_method_name = name
        return self.add_method(f, name)
    def build_method_map(self, prototype, prefix=''):
        """ Add prototype methods to the dispatcher.
        Parameters
        ----------
        prototype : object or dict
            Initial method mapping.
            If given prototype is a dictionary then all callable objects will
            be added to dispatcher.
            If given prototype is an object then all public methods will
            be used.
        prefix: string, optional
            Prefix of methods
        """
        if not isinstance(prototype, dict):
            prototype = dict((method, getattr(prototype, method))
                             for method in dir(prototype)
                             if not method.startswith('_'))
        for attr, method in prototype.items():
            if callable(method):
                self[prefix + attr] = method
if __name__ == '__main__':
    d = Dispatcher()
    d.add_method(lambda a, b: a + b, name="sum")
    d.add_method(lambda a, b: a - b, name="sub")
    d.add_method(lambda a, b: a * b, name="mul")
    @d.add_method(name="miclon")
    def method(*args, **kwargs):
        print(args, kwargs)
        return "miclon"
    @d.add_class
    class MyClass:
        def __init__(self):
            self.a = 1
        def method(self, b):
            return self.a + b
    
    @d.add_missing_method(name='__miss__')
    def missing_method(*args, **kwargs):
        print("未找到接收调用的方法", args, kwargs)
    print(d['sum'](1, 2))
    # 3
    print(d['miclon']('a', {'b': 'c'}, 'd'))
    # ('a', {'b': 'c'}, 'd') {}
    print(d['myclass.method'](2))
    # 3
    print(d['qqqqq'](2))
    # 未找到接收调用的方法
    

Dispatcher是一个类似字典的对象,它负责存储方法,并且提供一个字典存储方法的名称和方法的映射。

实际调用端可以通过方法名称来动态的调用方法,也可以通过方法名称来获取方法。

它没有任何限制,你要做的就是暴露公共的实例化Dispatcher类。

然后通过:add_method方法添加方法,add_class方法添加类,add_object方法添加对象,add_dict方法添加字典(字典中也是方法的名称和方法的映射),add_missing_method方法添加当引用一个不存在方法的时候的默认方法。


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档