原创作品:首发u3v3, 转载请保留

地址:https://www.u3v3.com/ar/1313

作者ID:Yi_Zhi_Yu

首发日期:2017.4.17

Python学习群:278529278 (欢迎交流)

PHP中的动态调用

PHP 中对对象方法可以方便的使用, 如下

<?php

class T
{
    public function a(){
        var_dump('Hello A');
    }
}

$t = new T();
$t->a();

$func = 'a';
$t->{$func}(); //这里就是动态调用 和 $t->a()调用一样

上面的例子可以看出, 通过动态调用, 我们可以以对象->函数名字符串()的方式动态的调用类对象

python 中的动态调用

PHP 中的动态调用如此简单, 那Python中呢

python 中没有像PHP 那样的通过字符串作为函数名调用的方式, 但python 有另外一种实现方式

我们知道, 在python的类中, 有这么一个特殊的函数 __getattr__

当我们通过obj.attr_name的方式获取obj中不存在的属性时, 就会触发该函数

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# Tony Wang @ 2017-04-18 00:31:12

class T():
    name = 'Yi_Zhi_Yu'

    def __getattr__(self, attr_name):
        return 'You are try get attr: {}'.format(attr_name)

if __name__ == '__main__':
    t = T()
    print(t.name) # Yi_Zhi_Yu
    print(t.age) # You are try get attr: age

那这和动态调用有什么关系呢,

在python 中, 类里面的的函数其实也是类的属性, 只不过类型比较特殊

理解了这一点, 我们看下面

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# Tony Wang @ 2017-04-18 00:31:12

class T():
    name = 'Yi_Zhi_Yu'

    """
    def __getattr__(self, attr_name):
        return 'You are try get attr: {}'.format(attr_name)
    """
    def __getattr__(self, func_name):
        return 'You are try get method: {}'.format(func_name)

if __name__ == '__main__':
    t = T()
    print(t.get_age) # You are try get method: get_age

现在我们已经能将get_age 动态的传递到类的对象中了, 下面就是调用函数了

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# Tony Wang @ 2017-04-18 00:31:12

class T():
    name = 'Yi_Zhi_Yu'

    """
    def __getattr__(self, attr_name):
        return 'You are try get attr: {}'.format(attr_name)
    """
    def __getattr__(self, func_name):
        def func():
            return 'You have call method: {}'.format(func_name)
        #return 'You are try get method: {}'.format(func_name)
        return func 

if __name__ == '__main__':
    t = T() 
    print(t.get_age()) # You have call method: get_age
    print(t.get_sex()) # You have call method: get_sex

现在我们就实现了函数的动态调用,

如果需要参数呢

其实就是定义的func函数里, 加上参数即可

    def __getattr__(self, func_name):
        def func(*args, **kwargs): # 这里的定义带了参数
            return 'You have call method: {}'.format(func_name)
        #return 'You are try get method: {}'.format(func_name)
        return func

应用场景

在python 中, 动态调用的实际的应用场景是什么

这里我举例说明一个

我们使用对redis进行操作的时候, 有时候需要添加一个代理类, 如下

class RedisClient():
    def init_con(self, *args, **args):
        # do init things
        # like connect redis
        pass

然后希望直接通过这个 RedisClient 进行redis操作, 比如 set, get, hget...,

redis_client.set(....)
redis_client.get(....)
....

这样调用的话, 就需要将pyredis中的所有函数都在RedisClient 中写一遍, 那就有点得不偿失了

这里实际就是希望能够做到动态调用, 将RedisClient 中的操作, 根据操作名, 直接映射到实际的pyredis操作

所以, 我们在RedisClient 中

class RedisClient():
    ....

    def __getattr__(self, func_name):
        def func(*args, **args):
            # 这里的 getattr 实际就相当于redis_con.<func_name>了
            return getattr(self.redis_conn, func_name)(*args, **args)

if __name__ == '__main__':
    redis_client = RedisClient()
    redis_client.init_con(.....)
    redis_client.set('key_name', 'key_value')

就实现了代理动态调动pyredis的操作的目的了

right