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

时间:2013-03-19 19:34:53

标签: python types metaclass inspect

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

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 ...

但是,想要在模块级别进行,而不是为每个函数执行此操作。每个函数可以有不同的参数。请注意,这不是函数重载。我在想写一个装饰者或一个元类。以下是我在这两种方法中遇到的问题: -

  1. 为所有功能使用通用装饰器:
    在这种方法中,我无法访问每个函数内定义的实际变量,因此废弃了这个想法。这是我打算写的一个闭包(用作装饰器):
  2. 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给出了一个非常好的建议 - 使用注释。我们可以使用Python 2.7中的某些东西吗?

2 个答案:

答案 0 :(得分:1)

通过装饰器这样做很容易 - 在Python 2中,您必须明确地装饰每个函数以注释类型 每个参数 - 或者你可以在doc-string上使用一些标记来使用一些注释, 并在模块的底部发出一个调用,它将迭代所有 模块上的对象,并将装饰器应用于每个函数 其中定义。

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

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'>
>>> 

答案 1 :(得分:-1)

我的倾向是这样的:

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方法(这应该在模块级或类级别工作,尽管为了清晰起见我可能更喜欢类。)

另外值得一提的是,这真的是单声道的。如果他们需要做不同的事情而不是将它们隐藏在函数构造函数中,那么在open中编写单独的函数通常会更加清晰。或者至少“更好地请求宽恕”并使用try / except块来封装仅适用于特定类型/类的操作。