使用单个接口验证不同函数的参数类型(不使用检查)

Posted

技术标签:

【中文标题】使用单个接口验证不同函数的参数类型(不使用检查)【英文标题】:validating argument types for different functions using a single interface (without using inspect) 【发布时间】:2013-03-08 16:15:13 【问题描述】:

我希望检查模块中每个函数的参数类型(不使用检查模块)。 我自己做过的最简单的解决方案是分别在每个函数中实现检查。

def func1( num1, num2 ):  # the two params must be integers
    if isinstance( num1, int ) and isinstance( num2, int ):
        # ...function code...
        pass
    else:
        return

def func2( float1 ):  # The only param must be float

    if isinstance( float1, float ):
        # ...function code...
        pass
    else:
        return

def func3( str1 ):  # string
    if isinstance( str1, str ):
        # ...function code...
        print 'fdfd'
        pass
    else:
        return


# and, so on ...

但是,想在模块级别做,而不是为每个功能做。每个函数可以有不同的参数。请注意,这不是函数重载。我正在考虑写一个装饰器或一个元类。以下是我在这两种方法中遇到的问题:-

    对所有功能使用通用装饰器: 在这种方法中,我无法访问每个函数内部定义的实际变量,所以放弃了这个想法。这是我打算写的一个闭包(用作装饰器):
def dec( funcName ):
    def typeChecker():
        i = __import__( __name__ )
        for m in map( lambda x: i.__getattribute__( x ), dir( i ) ):
            if '__call__' in dir( m ):  #Identifying a real function or callable which can be treated as function
                ## Now, that the function is identified, what should I do here to type-check the arguments??
    return typeChecker

请在此处提供一些见解,了解我如何才能完成这项工作。

2.使用元类创建函数 我只是想知道是否可以使用元类访问发送给函数的参数,然后验证每个参数,然后返回一个负责创建函数对象的全新类。但是,不知道该怎么做。这是解决这个问题的好方法吗?

Martijn Peters 给出的 1 个非常好的建议 - 使用注释。 Python 2.7 中有什么我们可以使用的吗?

【问题讨论】:

我不明白装饰器的论点。您可以访问修饰函数的参数,但不能访问内部变量。那么,你想要什么?函数的参数还是局部变量? 是的。那是正确的。我最初认为我可以访问内部参数。但是我错了。所以,我很快将注意力转移到元类上。 我不会用元类来做。尽量避免“魔术”。你考虑过依赖注入吗? 不,我是新手。你能解释一下吗。所有功能都没有单一的方法。我问这个的原因是模块内部有很多功能 为什么需要访问函数的局部变量来检查其参数的类型?装饰器是执行此检查的常用方法 【参考方案1】:

通过装饰器做到这一点相当容易 - 在 Python 2 中,您必须显式地装饰每个函数以注释类型 每个参数 - 或者您可以使用文档字符串上的一些标记来使用一些注释, 并在模块底部调用将遍历所有 模块上的对象,并将装饰器应用于每个函数 其中定义。

在这两种情况下,这样的装饰器就足够了:

from functools import wraps
from itertools import count

def typechecker(*types, **kw_types):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            for position, type_, arg in zip(count(), types, args):
                if not isinstance(arg, type_):
                    raise TypeError("Argument in position %s should be a %s"
                                    % (position, type_))
            for key, type_ in kw_types.items():
                if key in kw_types and not isinstance(kw[key], type_):
                    raise TypeError("Argument %s should be a %s"
                                    % (key, type_))
            return func(*args, **kw)
        return wrapper
    return decorator

你可以看到它是这样工作的:

>>> @typechecker(int, int)
... def mysum(a,b):
...    return a + b
... 
>>> mysum(2,3)
5
>>> mysum(2.0,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in wrapper
TypeError: Argument in position 0 should be a <type 'int'>
>>> 

【讨论】:

【参考方案2】:

我的倾向是这样的:

class A(object):
    def func(self, arg):
        result = self._get_func(arg)(arg)

    def _get_func(self, arg):
        # Create and return the appropriate function here.
        # You have a variety of options, e.g. arg.__class__ (if it exists),
        # or use a bunch of isinstance checks, etc.
        # Please bear in mind that this is really unpythonic!!
        if isinstance(arg, basestring):
            pass

如果你要经常调用函数,这显然是低效的,所以你会想要缓存你的特定函数,并尝试先从_get_func中的缓存中拉取。

def _get_func(self, arg):
    if arg.__class__ in self._cache: # Or whatever
        return self._cache[arg.__class__]
    else:
        pass # Create and return the appropriate function here.

当然,您可以根据需要重写 _get_func 方法(这应该在模块级别或类级别工作,尽管为了清晰起见我可能更喜欢类)。

另外值得一提的是,这真的很不合 Python。如果它们需要做不同的事情,通常只在公开的情况下编写单独的函数,而不是将它们隐藏在函数构造函数中,这通常要干净得多。或者至少,“最好请求原谅”并使用try/except 块来封装仅适用于特定类型/类的操作。

【讨论】:

你能解释一下我们如何验证/类型检查这里的每个参数吗?

以上是关于使用单个接口验证不同函数的参数类型(不使用检查)的主要内容,如果未能解决你的问题,请参考以下文章

Golang 函数传参使用切片而不使用数组为什么?

mapping的传参方式

可变参数省略号使用简介

如何让单个接口返回不同的数据类型?

对于实现接口的类型,你怎么能要求一个没有参数的构造函数呢?

QT 基础语法