在python中包装装饰器中的构造函数

问题描述 投票:0回答:2

我想有一个方便的装饰器,检查传递给方法的属性值是否不是None。然后我想在类方法中将它用作通用装饰器。为此,我写道:

def check_empty(name):
  def wrap_outer(function):
    def wrapped(*args, **kwargs):
      if kwargs.get(name, None) is None:
        raise Exception('{item} cannot be empty'.format(item=name))
      return function(args, kwargs)
    return wrapped
  return wrap_outer

并且,该类定义为:

class Player(object):
  @check_empty('name')
  def __init__(self, name):
    self.name = name

def __str__(self):
  return self.name

但是,这不起作用。构造函数和装饰器的模板不匹配。我们如何为构造函数创建函数装饰器?或者是基于类的装饰器更好的选择?


感谢@ ajax1234,我发现了问题。这是我传递给我的argskwargs的方式。应该是:

def check_empty(attr):
  def wrap_outer(function):
    def wrapped(*args, **kwargs):
      if (len(args) > 1 and args[1] is None) or (len(args)==1 and len(kwargs)==0):
        raise Exception('{item} cannot be empty'.format(item=attr))
      elif attr in kwargs and kwargs.get(attr, None) is None:
        raise Exception('{item} cannot be empty'.format(item=attr))
    return function(*args, **kwargs)
  return wrapped
return wrap_outer

如果参数作为关键字参数或仅作为位置参数传递,则负责检查参数。

注意:它确实有一个限制,即位置参数逻辑在保持通用性质的同时不能很好地扩展。 (我们需要使用inspect.getargspec查看位置参数的位置,然后从那里建立。)

python constructor python-decorators
2个回答
0
投票

你可以试试这个:

def check_empty(attr):
   def check_method(function):
     def wrapped(cls, **kwargs):
       if kwargs.get(attr) is None:
         raise Exception('{item} cannot be empty'.format(item=attr))
       return function(cls, **kwargs)
     return wrapped
   return check_method

class Player(object):
  @check_empty('name')
  def __init__(self, **kwargs):
    self.__dict__ = kwargs
  def __str__(self):
     return self.name

p = Player(name=None)

输出:

Exception: Name cannot be empty

但是,除了None之外的任何参数都有效:

p = Player(name = 'Foo')
print(p.name)

输出:

'Foo'

1
投票

我可以看到你能够做你想做的事情的唯一方法,包括位置和关键字参数,是提供位置到名称的映射,如下所示:

class NoneException(Exception):
    pass


def check_none(**check):
    def wrapper(fn):
        def wrapped(*args, **kwargs):
            for key, pos in check.items():
                try:
                    if kwargs[key] == None:
                        raise NoneException(f'{key} cannot be empty')
                except KeyError:
                    pass

                try:
                    if args[pos] == None:
                        raise NoneException(f'{key} cannot be empty')
                except IndexError:
                    pass

            return fn(*args, **kwargs)

        return wrapped

    return wrapper


class A:
    @check_none(name=1)
    def __init__(self, name):
        self.name = name

    @check_none(phrase=1)
    def test(self, phrase):
        print(f'My name is {self.name}. {phrase}!')


a = A('A')

a.test('Hello world!')

try:
    a.test(None)
except NoneException:
    print('successfully failed')
else:
    print('something went wrong')

这应该涵盖您在位置和明确命名时提供参数的情况,但请注意,考虑到必须保持位置参数索引的映射,这是一个脆弱的解决方案。

如果有一些方法可以反映出Python的内部映射self=0, name=1, et cetera,我会建议使用它。


或者,更简单的解决方案是在您要检查的每个函数的顶部进行函数调用。

class NoneException(Exception):
    pass

def check_none(*args, **kwargs):
    for key, value in (*enumerate(args), *kwargs.items()):
        if value == None:
            raise NoneException(f'key {key} == None')

class A:
    def __init__(self, name):
        check_none(name)

        self.name = name
© www.soinside.com 2019 - 2024. All rights reserved.