在元类的帮助下计算函数调用

介绍

计数函数调用

在您完成我们的元类简介一章之后,您可能已经问过自己有关元类的可能用例。有一些有趣的用例,但它不是 - 就像有人说的 - 一个等待问题的解决方案。我们已经提到了一些例子。

在 Python 教程的这一章中,我们想详细说明一个示例元类,它将装饰子类的方法。装饰器返回的装饰函数可以计算子类的每个方法被调用的次数。

这通常是我们期望分析器完成的任务之一。所以我们可以将这个元类用于简单的分析目的。当然,为进一步的分析任务扩展我们的元类会很容易。

初步说明

在我们真正深入研究问题之前,我们想提醒我们如何访问类的属性。我们将用列表类来演示这一点。我们可以使用以下构造获取类的所有非私有属性的列表 - 在我们的示例中是随机类。

import  random 
cls  =  "random"  # 类的名称作为字符串
all_attributes  =  [ x  for  x  in  dir ( eval ( cls )) 如果 不是 x 开始(“__” ) ]
打印(all_attributes )

输出:

['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'SystemRandom', 'TWOPI', '_BuiltinMethodType', '_MethodType', '_Sequence', '_Set', ' _acos', '_ceil', '_cos', '_e', '_exp', '_inst', '_log', '_pi', '_random', '_sha512', '_sin', '_sqrt', '_test' , '_test_generator', '_urandom', '_warn', 'betavariate', 'choice', 'exovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', '帕累托变量', 'randint', 'random', 'randrange', 'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']

现在,我们正在过滤可调用的属性,即类的公共方法。

方法 =  [ x  for  x  in  dir ( eval ( cls )) 如果 不是 x 开始(“__”  可调用(eval (cls  +  “。”  +  x ))]
打印(方法)

输出:

['Random', 'SystemRandom', '_BuiltinMethodType', '_MethodType', '_Sequence', '_Set', '_acos', '_ceil', '_cos', '_exp', '_log', '_sha512', ' _sin', '_sqrt', '_test', '_test_generator', '_urandom', '_warn', 'betavariate', 'choice', 'expovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate' , 'lognormvariate', 'normalvariate', 'paretovariate', 'randint', 'random', 'randrange', 'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', ' vonmisesvariate', 'weibullvariate']

通过否定可调用,即添加“not”,可以轻松获得类的不可调用属性:

non_callable_attributes  =  [ x  for  x  in  dir ( eval ( cls )) 如果 不是 x startswith ( "__" ) 
                              并且 不可 调用( eval ( cls  +  "."  +  x ))]
打印( non_callable_attributes )

输出:

['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'SG_MAGICCONST', 'TWOPI', '_e', '_inst', '_pi', '_random']

在正常的 Python 编程中,既不推荐也不需要按以下方式应用方法,但它是可能的:

lst  =  [ 3 , 4 ]
列表__dict__ [ "append" ]( lst ,  42 ) 
lst

输出:

[3, 4, 42]

请注意 Python 文档中的注释:

“因为 dir() 主要是为了在交互式提示下使用方便,所以它尝试提供一组有趣的名称,而不是尝试提供一组严格或一致定义的名称,并且它的详细行为可能会随着版本的不同而改变. 例如,当参数是一个类时,元类属性不在结果列表中。”

用于计算函数调用的装饰器

最后,我们将开始设计元类,这是我们在本章开头提到的目标。它将用装饰器装饰其子类的所有方法,该装饰器计算调用次数。我们在Memoization 和 Decorators一章中定义了这样一个装饰

def  call_counter ( func ): 
    def  helper ( * args ,  ** kwargs ): 
        helper 调用 +=  1
        返回 func ( * args ,  ** kwargs ) 
    helper 呼叫 =  0
    助手__name__ = 函数__名称__

    返回 帮手

我们可以按照通常的方式使用它:

@call_counter 
def  f ():
    通过

打印(f .调用)
for  _  in  range ( 10 ): 
    f ()
    
打印(f .调用)

输出:

0
10

如果您为装饰功能添加替代符号会更好。我们将在最终的元类中用到它:

def  f ():
    通过

f  =  call_counter ( f )
打印( f . call ) 
for  _  in  range ( 10 ): 
    f ()
    
打印(f .调用)

输出:

0
10

“计数呼叫”元类

现在我们拥有了所有必要的“成分”来编写我们的元类。我们将 call_counter 装饰器包含为一个静态方法:

class  FuncCallCounter ( type ): 
    """
        使用 call_counter 作为装饰器装饰子类的
所有方法的元    """
    
    @staticmethod 
    def  call_counter ( func ): 
        """ 装饰器,用于计算函数
            或方法调用函数或方法的次数 func 
        """ 
        def  helper ( * args ,  ** kwargs ): 
            helper 调用 +=  1
            返回 func ( * args ,  ** kwargs ) 
        helper 呼叫 =  0
        助手__name__ = 函数__名称__
    
        返回 帮手
    
    
    def  __new__ ( cls ,  clsname ,  superclasses ,  attributedict ): 
        """ 每个方法都用装饰器 call_counter 装饰,
            它会
attributedict 中attr进行实际调用计数        """ 如果可调用( attributedict [ attr ])不是attr . 开始(“__” ):attributedict [ attr ] = cls 呼叫计数器(   
                
                  attributedict [ attr ])
        
        返回 类型__new__ ( cls ,  clsname ,  superclasses ,  attributedict )
    

 (元类= FuncCallCounter ):
    
    def  foo ( self ):
        通过
    
    def  bar ( self ):
        通过

if  __name__  ==  "__main__" : 
    x  =  A () 
    print ( x . foo . call ,  x . bar . calls ) 
    x . FOO ()
    打印(X FOO 通话, X 酒吧呼叫)
    X 富() 
    x bar ()
    打印( x . foo .调用,  x 酒吧电话)
        

输出:

0 0
1 0
2 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值