如何在类内部自定义并使用装饰器?

如何在类内部自定义并使用装饰器?

问题描述:

想在类内部自定义装饰器log_transaction用来记录用户交易详情,但无法用@staticmethod和@classmethod修饰它,也不能让其作为普通方法,再作为装饰器调用


请问如何正确地在类内部自定义装饰器?

以下代码为何报错?

相关代码:

1.不用@staticmethod和@classmethod

class FundingManagement(object):
def __init__(self, initial_funding):
self.__funding = initial_funding

def log_transaction(self, func):
def inner(*args, **kwargs):
t = Transaction()
t.balance = func(*args, **kwargs)
t.date = datetime.datetime.now().strftime('%Y-%m-%d')
if func.__name__ == 'deposit':
t.kind = '存款'
elif func.__name__ == 'withdrawal':
t.kind = '取款'
elif func.__name__ == 'transfer':
t.kind = '网转'
t.value = args[1]
t.currency = '人民币'
t.print_transaction_details()
return

return inner

@log_transaction
def deposit(self, value):
self.__funding += value
return self.__funding

@log_transaction
def withdrawal(self, value):
self.__funding -= value
return self.__funding

@log_transaction
def transfer(self, value):
self.__funding += value
return self.__funding

相关截图:

http://img1.sycdn.imooc.com//climg/60ec4ec70948d48708560173.jpg


相关代码:

2.使用@classmethod

class FundingManagement(object):
def __init__(self, initial_funding):
self.__funding = initial_funding

@classmethod
def log_transaction(cls, func):
def inner(*args, **kwargs):
t = Transaction()
t.balance = func(*args, **kwargs)
t.date = datetime.datetime.now().strftime('%Y-%m-%d')
if func.__name__ == 'deposit':
t.kind = '存款'
elif func.__name__ == 'withdrawal':
t.kind = '取款'
elif func.__name__ == 'transfer':
t.kind = '网转'
t.value = args[1]
t.currency = '人民币'
t.print_transaction_details()
return

return inner

@log_transaction
def deposit(self, value):
self.__funding += value
return self.__funding

@log_transaction
def withdrawal(self, value):
self.__funding -= value
return self.__funding

@log_transaction
def transfer(self, value):
self.__funding += value
return self.__funding

相关截图:

http://img1.sycdn.imooc.com//climg/60ec4f6609c9c19e08370166.jpg


相关代码:

2.使用@staticmethod

class FundingManagement(object):
def __init__(self, initial_funding):
self.__funding = initial_funding

@staticmethod
def log_transaction(func):
def inner(*args, **kwargs):
t = Transaction()
t.balance = func(*args, **kwargs)
t.date = datetime.datetime.now().strftime('%Y-%m-%d')
if func.__name__ == 'deposit':
t.kind = '存款'
elif func.__name__ == 'withdrawal':
t.kind = '取款'
elif func.__name__ == 'transfer':
t.kind = '网转'
t.value = args[1]
t.currency = '人民币'
t.print_transaction_details()
return

return inner

@log_transaction
def deposit(self, value):
self.__funding += value
return self.__funding

@log_transaction
def withdrawal(self, value):
self.__funding -= value
return self.__funding

@log_transaction
def transfer(self, value):
self.__funding += value
return self.__funding

相关截图:

http://img1.sycdn.imooc.com//climg/60ec4fa2098d585409310199.jpg


尝试过的解决方式:

在FundingManagerment类外创建了Log类,将log_transaction作为Log类的静态函数

FundingManagerment类中使用@Log.log_transaction修饰那几个方法

这样虽然行得通,但是我认为这样破坏了封装性,自定义log_transaction的初衷就是只用来修饰FundingManagerment其他几个方法的,并不是一个通用的装饰器


相关代码:

class Log(object):

@staticmethod
def log_transaction(func):
def inner(*args, **kwargs):
t = Transaction()
t.balance = func(*args, **kwargs)
t.date = datetime.datetime.now().strftime('%Y-%m-%d')
if func.__name__ == 'deposit':
t.kind = '存款'
elif func.__name__ == 'withdrawal':
t.kind = '取款'
elif func.__name__ == 'transfer':
t.kind = '网转'
t.value = args[1]
t.currency = '人民币'
t.print_transaction_details()
return

return inner


正在回答

登陆购买课程后可参与讨论,去登陆

1回答

同学,你好!

(1)在相关代码1中,报错的原因是类型错误: log_transaction()缺少1个所需的位置参数:'func',在类内部自定义装饰器无需self参数

http://img1.sycdn.imooc.com//climg/60ed07ff09c657b403630256.jpg

(2)在相关代码2中使用@classmethod的情况下,报错原因是类型错误: 'classmethod'对象不可调用,在@classmethod下定义的函数是不能作为装饰器的

http://img1.sycdn.imooc.com//climg/60ed01ad092252d403490138.jpg

(3)在相关代码2中使用@staticmethod的情况下,报错原因是类型错误: 'staticmethod'对象不可调用,在@staticmethod下定义的函数也是不能作为装饰器的

http://img1.sycdn.imooc.com//climg/60ed022109d16c5603090140.jpg

(4)很不错,最后同学尝试过的解决办法很聪明,这个方法的确是破坏了类的封装性,在类内部自定义并使用装饰器可参考(1)

祝学习愉快!

  • 冰心本燃 提问者 #1

    追问两个问题:
    1.为何在类内部自定义的装饰器可以省去self参数?
    自己的猜测:只有在类外部,使用实例.成员方法()时,python会自动把这个实例作为这个成员方法的第一个位置参数传入,所以在定义时要提前留好这个位置参数。反之,如果这个方法不需要在类外部,以实例.成员方法()的方式调用,就可以在定义时省去self参数。以当前的需求来说,log_transaction()不应该在类外部使用,仅在类内部作为装饰器使用,这样看来是不是将其私有化更好?防止在类外,用实例误调用。所以扩大一点说,私有成员方法如果无需在类外部调用,也无需在类内部用self.私有成员方法()调用时,可以定义时不留self参数
    2. 那这样我就很奇怪了,这种方式下定义的__log_tranaaction()是一个非绑定方法,也就更像是静态方法,但是为什么不能用@staticmethod来修饰?但为什么在外部其他类中定义的@staticmethod修饰的静态方法,可以在FundingManagement类中作为装饰器使用?我不是很明白!请指教!谢谢!

    2021-07-13 13:17:51
  • 好帮手慕念 回复 提问者 冰心本燃 #2

    同学,你好!

    (1)是的,理解的非常到位,在类内部自定义装饰器,如果加self参数,必须经过实例化后才能使用。同学提出将log_transaction函数私有化的建议也是很好的

    (2)@classmethod和@staticmethod其下定义的函数即方法,如果被当做装饰器来装饰别的函数,是只能通过在其它类使用@类名.方法名或@实例名.方法名来装饰别的函数,在本身类内部是不能使用的

    (3)在同学尝试过的解决方式中,已经在其它类使用@类名.方法名。如下图,也是可以在其它类使用@实例名.方法名

    http://img1.sycdn.imooc.com//climg/60ed450d09fb95ea04820649.jpg

    祝学习愉快!

    2021-07-13 16:03:30
  • 冰心本燃 提问者 回复 好帮手慕念 #3

    ?不好意思老师,我还是没有理解,@classmethod和@staticmethod装饰的方法,如果被当做装饰器来装饰别的函数,静态的装饰器在类内部和在类外部有何区别?从报错位置以及描述'staticmethod' object is not callable来看,log_transaction或者是deposit在类内部似乎变成了一个staticmethod对象,导致不可调用。。。。。。为什么会出现这种原因,个人无法理解,望老师指教,非常感谢!

    2021-07-13 19:37:57
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师