Pythonのメソッドは、クラスメソッドの多くなる方法は?

著者|猫の下のエンドウ豆

Zebian |郭ルイ

Pythonでは、いくつかのライブラリのパラメトリックテストを実現し、より多くの方法の方法になることを行う方法であり、対応するパラメータを持つ各メソッドは、それをバインドしますか?

我々はそれを絞り込む:クラスでは、複数のクラスのメソッド(または類似の効果)にクラスの道にデコレータを使用する方法?

# 带有一个方法的测试类
class TestClass:
    def test_func(self):
        pass

# 使用装饰器,生成多个类方法
class TestClass:
    def test_func1(self):
        pass
    def test_func2(self):
        pass
    def test_func3(self):
        pass

本質的にはPythonのデコレータを装飾するための新しい代替アプローチで、偽りです。パラメータの実装の過程で、私たちはどのような手段/秘密の武器で最後のいくつかのライブラリを導入しましたか?

どのようにパラメータを実現DDT?

ライブラリDDT書かれた前回の記事ではまず見て:

import unittest
from ddt import ddt,data,unpack
@ddt
class MyTest(unittest.TestCase):
    @data((3, 1), (-1, 0), (1.2, 1.0))
    @unpack
    def test(self, first, second):
        pass

DDTは4つのデコレータを提供します:@ddtカテゴリに適用され、3つの方法がありますデータ@クラスに適用され、アンパックおよび@file_data @(先に言及されていません)。

3つのデコレータに適用されるクラスメソッドの役割を見てみましょう:

# ddt 版本(win):1.2.1
def data(*values):
    global index_len
    index_len = len(str(len(values)))
    return idata(values)

def idata(iterable):
    def wrapper(func):
        setattr(func, DATA_ATTR, iterable)
        return func
    return wrapper

def unpack(func):
    setattr(func, UNPACK_ATTR, True)
    return func

def file_data(value):
    def wrapper(func):
        setattr(func, FILE_ATTR, value)
        return func
    return wrapper

それらの相互作用は、()メソッドでは、class属性を追加することSETATTRです。これらの特性として何時に使用するには?クラスプラスソースデコレータ@ddtで見てみましょう:

クラスメソッドのすべてをループするための第一の層、及び2つのソースのIF /二つのブランチ、それぞれDATA_ATTR / FILE_ATTR、ELIFすなわち、対応するパラメータ:データ(@data)とファイル(@file_data)。

枝論理解析elifのファイル、類似したとのデータを処理した後、私たちは前のブランチ場合は主に確認するために、それをスキップして。ロジックのこの部分は、以下のようにメインタスクがあり、非常に明確です:

  • メソッドのクラスキーパラメータを横断

  • はい、オリジナルのメソッド名とパラメータに基づいて、新しいメソッドを作成します

  • 元のメソッドのドキュメンテーション文字列を取得

  • 開梱のためのパラメータの種類のタプルとリスト

  • テストクラスの新しいテストメソッドを追加し、文書の文字列をバインドパラメータ

ソースコード解析、それは、3つの主要なパラメータは、プロパティを通過し、設定@file_dataアンパックおよびデコレータ@、@データ、見られ、処理ロジック@ddtデコレータのコアであることができます。

そのような装飾は、分散(プラス各クラス及びクラスメソッドで)、その後、非常にエレガントな使用方式を組み合わせました。なぜそれを使用するために団結することはできませんか?その後、我々はそれがあまりにも厄介な、最初のリフレインは、何の他の実装を見て分析するのだろうか?

パラメータ化パラメータ化を達成するためにどのように?

先回顾一下上篇文章中 parameterized 库的写法:

import unittest
from parameterized import parameterized
class MyTest(unittest.TestCase):
    @parameterized.expand([(3,1), (-1,0), (1.5,1.0)])
    def test_values(self, first, second):
        self.assertTrue(first > second)

它提供了一个装饰器类 @parameterized,源码如下(版本 0.7.1),主要做了一些初始的校验和参数解析,并非我们关注的重点,略过。

我们主要关注这个装饰器类的 expand() 方法,它的文档注释中写到:

A "brute force" method of parameterizing test cases. Creates new test cases and injects them into the namespace that the wrapped function is being defined in. Useful for parameterizing tests in subclasses of 'UnitTest', where Nose test generators don't work.

关键的两个动作是:“creates new test cases(创建新的测试单元)”和“inject them into the namespace…(注入到原方法的命名空间)”。

关于第一点,它跟 ddt 是相似的,只是一些命名风格上的差异,以及参数的解析及绑定不同,不值得太关注。

最不同的则是,怎么令新的测试方法生效?

parameterized 使用的是一种“注入”的方式:

inspect 是个功能强大的标准库,在此用于获取程序调用栈的信息。前三句代码的目的是取出 f_locals,它的含义是“local namespace seen by this frame”,此处 f_locals 指的就是类的局部命名空间。

说到局部命名空间,你可能会想到 locals(),但是,我们之前有文章提到过“locals() 与 globals() 的读写问题”,locals() 是可读不可写的,所以这段代码才用了 f_locals。

pytest 如何实现参数化?

按惯例先看看上篇文章中的写法:

import pytest
@pytest.mark.parametrize("first,second", [(3,1), (-1,0), (1.5,1.0)])
def test_values(first, second):
    assert(first > second)

首先看到“mark”,pytest 里内置了一些标签,例如 parametrize、timeout、skipif、xfail、tryfirst、trylast 等,还支持用户自定义的标签,可以设置执行条件、分组筛选执行,以及修改原测试行为等等。

用法也是非常简单的,然而,其源码可复杂多了。我们这里只关注 parametrize,先看看核心的一段代码:

根据传入的参数对,它复制了原测试方法的调用信息,存入待调用的列表里。跟前面分析的两个库不同,它并没有在此创建新的测试方法,而是复用了已有的方法。在 parametrize() 所属的 Metafunc 类往上查找,可以追踪到 _calls 列表的使用位置:

最终是在 Function 类中执行:

好玩的是,在这里我们可以看到几行神注释……

阅读(粗浅涉猎) pytest 的源码,真的是自讨苦吃……不过,依稀大致可以看出,它在实现参数化时,使用的是生成器的方案,遍历一个参数则调用一次测试方法,而前面的 ddt 和 parameterized 则是一次性把所有参数解析完,生成 n 个新的测试方法,再交给测试框架去调度。

对比一下,前两个库的思路很清晰,而且由于其设计单纯是为了实现参数化,不像 pytest 有什么标记和过多的抽象设计,所以更易读易懂。前两个库发挥了 Python 的动态特性,设置类属性或者注入局部命名空间,而 pytest 倒像是从什么静态语言中借鉴的思路,略显笨拙。

小结

回到标题中的问题“如何将一个方法变为多个方法?”除了在参数化测试中,不知还有哪些场景会有此诉求?欢迎留言讨论。

本文分析了三个测试库的装饰器实现思路,通过阅读源码,我们可以发现它们各有千秋,这个发现本身还挺有意思。在使用装饰器时,表面看它们差异不大,但是真功夫的细节都隐藏在底下。

作者:豌豆花下猫,生于广东毕业于武大,现为苏漂程序员,有一些极客思维,也有一些人文情怀,有一些温度,还有一些态度,公众号「Python猫」(python_cat)。

声明:本文系作者投稿,版权归作者所有。

你点的每个“在看”,我都认真当成了喜欢

发布了1652 篇原创文章 · 获赞 4万+ · 访问量 1386万+

おすすめ

転載: blog.csdn.net/csdnnews/article/details/104067234