python契约装饰器

pcd的Python项目详细描述


|管道状态

…图片::img/logo.png?raw=true
:alt:pcd


python合同装饰装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修装修gt;``测试`_
-`license<;license>;``uuu


抽象
——



契约编程可以提高性能,增加自文档覆盖率
,总之是一种有用的代码*强化*技术。主要原则是,程序中的所有组件都应该就它们之间的交互方式达成一致。因此,不必使用所有必须保证自身正确性的独立组件,而是使用契约编程来保证整个组件块作为一个单元的正确性。

veral库试图将相同的功能添加到python中。
遗憾的是,它们要么是坏的,要么是不完整的,要么是非常重的,或者只是通过向python引入外来语法来重新设计轮子。幸运的是,在python中,
几乎总是一种简单易行的方法,这正是"pcd"所提供的功能。

总之,足够抽象的"mumbo jumbo"了,让我们开始讨论它是如何工作的,好吗?像往常一样,通过简单而虚拟的例子更容易理解发生了什么。

代码::python


def get_user_input():
input={}
为真时:
尝试:
键,value=raw_input('<;键>;,<;值>;或<;返回>;:')。拆分(',')
输入[键]=值
除了值错误:
break
return process_input(input)

def process_input(input):
cleaned={}
对于key,输入中的值。items():
cleaned[clean_value(key)]=clean_value(value)
return cleaned

def clean_value(value):
return value.strip()

代码::python


def get_user_input():
input={}
为真时:
尝试:
键,value=raw_input('<;键>;,<;值>;或<;返回>;:')。拆分(',')
输入[键]=值
除了值错误:
中断
对象
尝试:
对于键,输入中的值。items():
尝试:
已清理[清理值(键)]=清理值(值)
除了类型错误:
继续
除了属性错误:
提高Typeerror('无效输入类型')
return cleaned

def clean廑value(value):
廑如果value不是str-like对象
请尝试:
return value.strip()
,attributeerror除外:
raise typeerror('无效值类型')

,这种方法有两个缺点。一方面,代码现在乱七八糟,
因为所有的错误检查都分布在各个函数中,使得理解每个函数试图解决的确切问题变得更加困难。另一方面,引入的错误处理机制导致不必要的开销(有时甚至在看似不相关的地方进行冗余检查),也就是说,无论已经检查过的输入数据是否正确,检查都在运行。

如果我们能够确保使用另外两个组件的顶级组件能够保证输入的正确性,那么就完全没有必要引入上面所示的错误处理:

代码::python

from pcd import contract

@contract(post=lambda r:isinstance(r,dict))
def get_user_input():
input={}
为真时:
尝试:
键,value=raw_input('<;键>;,<;值>;或<;返回>;:')。拆分(',')
input[键]=value
除了值错误:
中断
返回进程输入(input)

@contract(pre=lambda:isinstance(input,dict),
post=lambda r:isinstance(r,dict))
定义进程输入(input):
已清理={}
对于键,输入中的值。items():
已清理[clean]n_value(key)]=clean_value(value)
return cleaned

@contract(pre=lambda:isinstance(value,str)或
isinstance(value,unicode))
def clean_value(value):
return value.strip()

结果更清晰,更易于阅读和理解,最重要的是在相同的时间
e它也有条件地存在,并且可以在不
再次触摸代码的情况下删除。因此,在启用
契约的情况下对程序进行了大量测试之后,可以通过去掉
装饰器来极大地优化应用程序。在开发阶段,如果另一个组件想要使用一个已经*签约*的组件,那么该组件必须遵守它的合同,
这正是装饰人员要确保的。

但这还不是全部!契约编程可以与*类*一起使用。实际上,它可以非常有效地用于维护数据完整性,这是使用用户定义类型的最重要方面之一。当然
可以在类的选定方法上使用上面解释的契约,但是
``pcd``通过实现*不变量提供了更好的结果*

类的y``property`。它们由
子类继承,也可以与手动定义的契约结合起来。

代码::python



def\uu init(self,a,b,c):
如果a<;=0或b<;=0或c<;=0:
升高值错误('所有边都需要大于0')
elif a+b<;=c或a+c<;=b或b+c<;=a:
升高值错误(
"任何两个边的总和必须大于第三个边的总和")
self.a=a
self.b=b
self.c=c

@property
def area(self):
pass;这里是heron公式的实现

定义(并且
然后使用)*public*边名称。因为即使我们在初始化过程中确保了所有边都有有效的大小,因此我们可以安全地调用其上的"area"方法,不幸的是,我们不能保证数据的完整性,因为任何人都可以自由地将"1"分配给任何边。因此,
有人试图重写上述内容如下:

…代码::python




def初始(self,a,b,c):
self.\u验证(a,b,c)
self.。_a=a
自。_b=b
自。_c=c

@静态方法
定义验证(a,b,c):
如果a<;=0或b<;=0或c<;=0:
增大值误差('所有边都需要大于0')
elif a+b<;=c或a+c<;=b或b+c<;=a:
提高值错误(
"任何两个边的和必须大于第三个边的和")


@property
def a(self):
return self.\u a
@a.setter
def a(self,value):
自我验证(value,self.-b,self.-c)
self.-a=value

@property
def b(self):
return self.-b
@b.setter
def b(self,value):
self.-validate(self.-a,value,self.-c)
self.-b=值

@property
def c(self):
return self.\u c
@a.setter
def c(self,value):
self.\u validate(self.\u a,self.\u b,value)
self.\u c=value

@property
def area(self):
pass这里是heron公式的实现

它还与程序逻辑混合在一起。不仅如此,如果"三角形"只在内部使用,也就是说,这些值不是来自用户输入,而是来自可信的源,那么无论如何,检查都是冗余运行的,在每个分配上都会产生很大的开销。e带*不变量*:

…代码::python

self.c,
lambda:self.a+self.c>;self.b,
lambda:self.b+self.c>;self.a)


def初始(self,a,b,c):
self.a=a
self.b=b
self.c=c

@property
def area(self):
pass这里是heron公式的实现

这意味着,我们可以意外地将一个边重写为一个无效值,但是下次我们希望在方法中使用该值时,将立即对其进行检查。如果这被认为是一个太大的风险,那么还可以为每个值定义getter和setter,并为它们生成验证器。是的,*不变量*可以随时打开或关闭,而无需更改代码。甚至更多!它们也是遗传的!

有关合同编程的更多信息,请阅读此
`wikipedia<;https://en.wikipedia.org/wiki/design嫒u contract>;`嫒u嫒br/>文章。

代码::bash


…代码::bash

pip install-r requirements\u dev.txt


要运行测试,请使用"pytest"`:

……代码::bash

pytest tests.py

Rlt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;i>;lt;lt;lt;lt;i>;lt;lt;lt;lt;b>;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;gt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;gt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt可调用项<;/i>;],
<;b>;mut<;/b>;=[<;i>;可调用<;/i>;或<;i>;可调用<;/i>;]<;i>;)<;/i>;<;/code>;<;/pre>;

"pre"应包含修饰函数的所有*先决条件。
每个*可调用*不带参数,您可以使用修饰函数定义的
参数名。每个*可调用*都可以看到所有参数。

``post``应该包含修饰函数的所有*后条件*。
每个*可调用*都有一个参数,可以自由命名。此参数将包含修饰函数返回的值。每个*可调用*也可以查看修饰函数的所有
参数。

` mut`应该包含*可变*参数的所有*后条件。
如果修饰函数通过其
参数有副作用,则这非常有用。每个*callable*不接受参数,并且可以使用修饰函数定义的相同参数
名称。在函数返回后调用检查。每个*可调用*都可以看到所有参数。

如果"调试"为"真",则"协定"无效。

raw::html

<;pre>;<;code>;<;b>;不变量<;/b>;<;i>;(<;/i>;<;/code>;<;/pre>;

不变量类可以用作*元类*。如果发生这种情况,则该类的"条件"将在"初始化"方法之后、"删除"方法之前以及每次调用公共方法和"属性"之前和之后自动执行。每个条件都可以获得对"self"变量的
访问。

----


要删除检查,请使用
优化:

运行程序。代码::bash

$python-o sample.py

--


**警告!**决不能在"contract"条件内更改
修饰函数的参数或返回值,因为这些
可能是可变值,因此删除这些契约将改变函数的
行为,并可能导致意外行为!同样的
对于不变条件来说,在这些回调中决不能对任何
进行变异!

性能
——

"perf.py"中的
示例表明,这两个函数的字节码指令数量相同,执行时间也相同。

由于参数处理
和由"contract"decorator完成的注入而引起的警告。但是这样做使得
在大多数情况下很难检查
修饰函数的返回值和/或副作用,而"contract"是一种方便的方法。

testing
----


contract programming与单元测试配合得很好。事实上,强烈建议测试合同,以及代码的一般行为。代码::python

from pcd import contract
from pytest import引发

@contract(pre=lambda:len(name)>;0)
def store(name):
/>#

n正确的数据
assert srore_name('hello')==5


许可证
----


版权所有©2017`peter varo<;http://www.petervaro.com>;`\uuu

此程序是免费软件:您可以在免费软件founda发布的GNU Lesser General Public许可证的条款下重新分发和/或修改它。第3版许可证,或(由您选择)任何
更高版本。

本程序的发布是希望它将是有用的,但没有任何
保证;甚至没有针对
特定用途的适销性或适用性的隐含保证。有关更多详细信息,请参阅gnu lesser general public license。

您应该已经收到gnu lesser general public license的副本以及此程序的
。如果没有,请参见http://www.gnu.org/licenses






版权版权版权版权版权版权版权版权版权版权版权版权版权版权版权版权版权版权版权版权;http://www.petervarro.com>;`


4.0 international
4.0 international
4.0 international
>license<;https://creative commons.org/licenses/by sa/4.0>;` ` ` ` 4.0>;` ` ` ` ` ` ` ` ` ` ` `……|管道状态图像::https://gitlab.com/petervaro/pcd/badges/master/pipeline.svg
:目标:https://gitlab.com/petervaro/pcd/commits/master
。|许可证图像::https://i.creativecommons.org/l/by sa/4.0/80x15.png
:目标:https://creativecommons.org/licenses/by sa/4.0

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java无法在spring boot应用程序中启用本机内存跟踪   jakarta ee在Java Web项目上的多窗口   日期将Java时间戳转换为MySQL时间戳,反之亦然   java如何实现异步任务连接到服务器并解析JSON   java为什么我得到索引越界异常?   我们如何在java中以大写字母和小写字母存储同名文件   jni/java:有效不可变本机对象的线程安全发布/共享   Java将文本写入远程文件   int最小硬币算法   java如何设置/获取我在类Vehicle的主方法中创建的类Car的“ford”实例的名称?   java使用计时器在队列已满时重新调度使用者   java从字符串的末尾提取一个子字符串,直到遇到第一个空格为止?   java在SimpleApplication之外正确初始化物理状态