假设我有一个名为 generator
返回一个在特定范围内随机选择的4元组。假设这个元组的形式是这样的 (age, sex, location, marital_status)
:
age is in range(5, 85)
sex is a member of the set {"m", "f"}
location is a member of the set of all the cities in California
marital_status is a member of {"married", "single", "separated"}
另一方面,假设我定义了20个不同的函数,其定义如下。
def p1 (age, sex, location, marital_status)
def p2 (age, sex, location, marital_status)
.
.
其中 p1
应该接收参数,其值的形式如下。
`age` must be in the range 20 to 45
`sex` must be male
`location` could be any city in Southern California
`marital_status` could be either single or married
想象一下 p2
一直到 p20
.
有什么实用的方法来确定哪一组生成的值符合哪个函数?
在这种情况下,所有的定义都是完全一样的,但我可以想象到在某些情况下,定义可能会有细微的差别,比如说 p18
可能是 def p1 (age, location)
的可能性范围的具体限制。age
和 location
.
P.S. 这些模式不一定是相互排斥的,这意味着一组生成的值可能会匹配多个函数。
在Python 3.X (但不是2.X)中,你可以在Python 3.X中附加以下内容 注释信息 (关于函数的参数和结果的任意用户定义数据)到一个函数对象。在这里,你可以在装饰器中使用这个功能来包装你的函数,以检查你的参数的范围。
例如,你可以使用以下范围测试函数。
def rangetest(func):
def onCall(**kargs):
argchecks = func.__annotations__
if all(val in range(*argchecks.get(arg)) for arg,val in kargs.items()):
return func(**kargs)
else :
print ("invalid arg range")
return onCall
@rangetest
def func(a:(1, 5), b:(4,7), c:(0, 10)):
print(a + b + c)
Demo :
func(a=2, b=6, c=8)
16
func(a=2, b=6, c=15)
invalid arg range
这里有一些要点。第一,由于注解信息是在一个字典中(python以字典的形式返回),而字典没有特定的顺序,你需要在你的函数中使用关键字参数,才能得到它在注解信息字典中的相对范围。
另外,这里我只是使用了数字范围,但你也可以使用一些自定义范围,比如像你问题中显示的单词列表。all
你需要检查其类型,然后根据其类型使用适当的操作 。
all(kwargs.get(arg) in range(*arg_range) if is instance (arg_range,tuple) else kwargs.get(arg) in arg_range for arg,arg_range in argchecks.items())
# define test t1 for function p1
def t1(params):
return params["age"] in range(5, 85) \
and params["sex"] in ["m", "f"] \
and cityof(params["location"], "California") \
and params["marital_status"] in ["married", "single", "separated"]
# and similarly for other p* functions
# define functions
def p1(params): ...
def p2(params): ...
# and so on
# bind tests and functions
RULES = {
(t1, p1),
(t2, p2),
...
}
# have the right functions called
def go(params):
for rule in RULES:
if rule[0](params):
rule[1](params)
# test it
go({"age": 6, "sex": "m", "location": "somewhere", "marital_status": "single"})
只是一些注释。
其实有几种可能的变化,但主要原理是一样的。找到匹配的函数并调用它.
如果你愿意在你的函数中添加格式化的 doc 字符串(而不是检查每个参数的类型),那么你可以考虑这样做。
# This function has a foratted doc string.
# :argument: Truth condition to be evaluated
# If any condition is False, function won't be called
def party_list(name, age, sex):
"""
:name:"{}" != "Dad"
:age:17< {} <25
:sex:True
"""
print("You're invited to my party, {}!".format(name))
# We will make some sample data
keys = ["name", "age", "sex"]
values = [["John", 24, "Male"],
["Sarah", 25, "Female"],
["Pikachu", 2, "Pokemon"],
["Pizza guy", 18, "Male"]]
# Turn it in to a dictionary
for key, value in enumerate(values):
values[key] = {t:p for t, p in zip(keys, value)}
# These conditions can be dynamically made for each function,
# because we have access to the doc string from the outside
conditions = list(filter(lambda c: ':' in c, party_list.__doc__.split('\n')))
for friend in values:
for case in conditions:
tag, code = case.split(':')[1:]
if not eval(code.format(friend[tag])):
break
else:
party_list(friend["name"], friend["age"], friend["sex"])