class Account:
def __init__(self, **kwargs):
[...]
self._is_valid = True
def valid_before_logout(self, func):
def execute(*args, **kwargs):
if self._is_valid:
return func(*args, **kwargs)
else:
raise AccountOperatingError('the account is already invalidated or signed out')
return execute
@valid_before_logout
def refresh(self):
[...]
@valid_before_logout
def invalidate(self):
self._is_valid = False
[...]
@valid_before_logout
def signout(self):
self._is_valid = False
[...]
类Account
里面有一个方法valid_before_logout
用作装饰器,作用是在执行任何类方法之前检查self._is_valid
,如果为False
就抛出异常。
不过在导入这个模块的时候出错:
>>> import account
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "account.py", line 1, in <module>
class Account:
File "account.py", line 15, in <module>
def refresh(self):
TypeError: valid_before_logout() missing 1 required positional argument: 'func'
>>>
请问这是什么原因?
1
casparchen 2021-09-27 23:10:51 +08:00 via iPhone
@self.valid_before_logout
|
2
jaredyam 2021-09-27 23:13:48 +08:00
Traceback 不是说的很明显么,你在类里面把装饰器作为正常装饰器用了,却还给了个 self,所以就还缺个 func 。
要么 @self.decorator 要么 @staticmethod def decorator()。 |
3
MiketsuSmasher OP @casparchen
@jaredyam 换成 @self.valid_before_logout 之后仍然出错: ``` Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/menacing/Projects/mukago/ignored/account.py", line 5, in <module> class Account: File "/home/menacing/Projects/mukago/ignored/account.py", line 19, in Account @self.valid_before_logout NameError: name 'self' is not defined ``` |
4
hsfzxjy 2021-09-27 23:17:30 +08:00
... def valid_before_logout(func):
... ... def execute(self, *args, **kwargs): |
5
rpman 2021-09-28 01:07:19 +08:00 via iPhone
你装饰器的写法是错的
要把装饰器写在类里的方法: https://medium.com/@vadimpushtaev/decorator-inside-python-class-1e74d23107f6 当然我觉得写在类外也很合理 |
6
weyou 2021-09-28 02:05:27 +08:00 via Android
不确定,decorator 只能放在类外吧,可以这样做
def valid_before_logout(func): … def wrapper(self, *args, **kwargs): … … if self._is_valid: … … … return func(self, *args, **kwargs) … … else: … … … raise AccountOperatingError('the account is already invalidated or signed out') … return wrapper class Account: … def __init__(self, **kwargs): … … [...] … … self._is_valid = True … @valid_before_logout … def refresh(self): … … [...] |
7
O5oz6z3 2021-09-28 06:26:55 +08:00
原因是在类定义内使用 valid_before_logout 时,就是个普通函数,不是实例方法,所以会缺少 self 。就和 cls.method() 是一样的,会报错缺少 self 。一开始我也以为装饰成静态方法或类方法就解决了,没想到这种方法不兼容直接调用。
想到一个简单的思路兼容两种情况,不过没有试验过: def valid_before_logout(self, func=None): ... func = func or self ... #... |
8
Varchar 2021-09-28 08:44:58 +08:00
def valid_before_logout(func):
def execute(*args, **kwargs): if args[0]._is_valid: |
9
SjwNo1 2021-09-28 09:09:20 +08:00
你这是个带参装饰器,你需要填充 self
|
10
Ritter 2021-09-28 09:18:21 +08:00
定义在类外面就简单多了
|
11
2i2Re2PLMaDnghL 2021-09-28 09:50:27 +08:00
你对于函数调用模型含混不清。
写成 C-like OOP 再看看 @decorator def func 语法实质上是 func=decorator(__temp_func) 无论写在 class 里还是 class 外都一样。 而另一方面,你的 decorator 返回的东西根本不接受 self 按你的思路应该把这些东西全部放进 __init__ 里去 |
12
Pzqqt 2021-09-28 09:58:02 +08:00
@weyou #6 @MiketsuSmasher 此乃正解,不过把 valid_before_logout 定义在 Account 类里边也是可以的,但不能加 staticmethod 装饰器(此时 valid_before_logout 既不是类方法也不是实例方法,它可以在外部通过类名直接调用,但不与类交互更不与实例交互,相当于类属性)。
这样会带来一个新的问题:如果你要继承 Account 类并重写被 valid_before_logout 装饰过的方法,除非显式调用超类方法或着重新装饰该方法,不然装饰器会失效。举例: ....class B(Account): ........def signout(self): ............self._is_valid = False ....b = B() ....b._is_valid # True ....b.signout() ....b._is_valid # False ....b.signout() # 此时应该触发异常, 但并没有, 因为该方法已经不再被 valid_before_logout 装饰 |
13
2i2Re2PLMaDnghL 2021-09-28 10:15:26 +08:00 1
|
14
killva4624 2021-09-28 15:42:39 +08:00
我也试过这样的用法,还是写在类外面比较合适,比如一个 requests 的接口工具类:
def request_verify(func) -> dict: """验证请求是否正常,如果异常直接抛错""" @wraps(func) def inner(cls, *args, **kwargs): response = func(cls, *args, **kwargs) response.raise_for_status() return response.json() return inner |
15
imn1 2021-09-29 13:21:20 +08:00
self 和 func 是两个参数,两个都不能省,所以你的装饰器是个必须带参数的装饰器,因为不带参数的话,第一个传入的参数的内容是一个函数,但却赋给了 self 了
|
16
featureoverload 2021-10-13 15:31:01 +08:00
|