Python装饰器继承问题
Posted
技术标签:
【中文标题】Python装饰器继承问题【英文标题】:Python Decorator Inheritance issue 【发布时间】:2022-01-18 05:03:41 【问题描述】:class Foo():
help_message = ""
def Help_Decorator(func):
def wrapper(data, context, caller):
try:
if data[0] == "help":
return(help_message) #<--- cannot access this locally
else:
return func(data,context,caller)
except:
return func(data,context,caller)
return wrapper
class Bar(Foo):
help_message = "A real help message!"
@foo.Help_Decorator
def Run(data,context,caller):
pass
如何在我的 Help_Decorator 中访问 Bar 的 help_message 而不将其作为参数传递给 Run
?
【问题讨论】:
你不能。foo.Help_Decorator
仅获取一个函数作为其参数,并且该函数没有任何对 Bar
的引用,您可以使用它来访问 Bar.help_message
。
为什么Run
data
的第一个参数?应该是self
。无论如何,现在该实例将被传递给 第一个参数,所以data.help_message
应该可以工作,但您似乎根本不了解类定义
这样永远行不通。尝试在class Foo
中执行def foo(self): print(help_message)
。
根据您的描述,听起来您只需要模块中的常规功能。这里的课似乎没有用
【参考方案1】:
由于Run
是一个实例方法,data
是一个Bar
实例。通常这会按照约定称为self
,但给它一个不同的名称不会改变函数调用的语义。
因此,您应该能够在您的wrapper
函数中访问data.help_message
。 (我也希望data[0]
给你一个TypeError
,除非Bar
已经实现了__getitem__
。)
如果您的意图是在类而不是实例上调用Run
,则不应将其定义为实例方法。使其成为类方法:
class Bar():
help_message = "A real help message!"
@classmethod
@foo.Help_Decorator
def Run(cls, data, context, caller):
pass
现在你可以在你的包装器中做:
def Help_Decorator(func):
def wrapper(cls, data, context, caller):
try:
if data[0] == "help":
return(cls.help_message)
else:
return func(cls, data, context, caller)
except:
return func(cls, data, context, caller)
return wrapper
请注意,Help_Decorator
不需要成为Foo
或Bar
的方法。
大家一起:
class Foo():
help_message = ""
def Help_Decorator(func):
def wrapper(cls, data, context, caller):
try:
if data[0] == "help":
return(cls.help_message)
else:
return func(cls, data, context, caller)
except:
return func(cls, data, context, caller)
return wrapper
class Bar(Foo):
help_message = "A real help message!"
@classmethod
@Foo.Help_Decorator
def Run(cls, data, context, caller):
return "derp"
print(Bar.Run(["help"], 1, 0))
打印:
A real help message!
完全从代码中提取Foo
会产生完全相同的结果;实现Help_Decorator
的地方和它装饰的方法的类之间不需要任何继承关系:
def Help_Decorator(func):
def wrapper(cls, data, context, caller):
try:
if data[0] == "help":
return(cls.help_message)
else:
return func(cls, data, context, caller)
except:
return func(cls, data, context, caller)
return wrapper
class Bar:
help_message = "A real help message!"
@classmethod
@Help_Decorator
def Run(cls, data, context, caller):
return "derp"
print(Bar.Run(["help"], 1, 0))
【讨论】:
data[0] 是我的初始函数参数的一部分,不会返回 TypeError。我没有实例化这些类的任何实例。 @Situc 那么为什么它是一个类,你希望函数如何访问类变量? @Sitluc 您可能希望将其设为@classmethod
,以便它接收cls
作为第一个参数。将其设为实例方法,然后将不是实例的内容作为第一个参数传递(a)对试图理解您的代码的其他人不友好(b)与您在这里尝试做的事情适得其反。
你必须得到cls.help_message
。如果函数是在Bar
上定义的类方法,则cls
应该是Bar
。
测试过了,需要先来。装饰器排序总是令人困惑。 @Sitluc 我用实际的工作代码和示例输出更新了它。运行它并告诉我它不起作用。 :P【参考方案2】:
传递给包装器的第一个参数是 Run 类的实例,您应该添加 self 作为第一个参数。然后你可以参考self.help_message,它会返回Run类中定义的help_message。
(编辑) 如果您想将装饰器用作类方法,那么只需对其进行装饰即可。你的例子会变成。
class foo():
help_message = "bad"
@classmethod
def Help_Decorator(cls, func):
@classmethod
def wrapper(cls, data, context, caller):
print(data, context, caller)
try:
if data[0] == "help":
print('helping', cls)
return(cls.help_message) #<--- cannot access this locally
else:
return func(data,context,caller)
except:
return func(data,context,caller)
return wrapper
class Bar():
help_message = "A real help message!"
@foo.Help_Decorator
@classmethod
def Run(cls, data,context,caller):
pass
【讨论】:
我应该更清楚。我没有实例化任何一个类的实例。 @Sitluc 那么它不应该是一个类。类的唯一目的是创建实例。如果这不是你使用它们的目的,那么你不应该期望类对你有用,就像你在这里遇到的那样。我不确定您希望如何访问类状态。 我相信你是不正确的。我从 Foo 继承来节省自己重写许多函数的时间。我如何错误地使用类? 为什么要重写任何函数? 未实例化的类没有任何用途,除了命名空间之外,但这可以通过types.SimpleNamespace
轻松完成,或者只是模块中的函数。如果你没有实例化任何东西,继承的目的是什么?
如果您想使用类方法而不是实例化,已编辑以包含答案以上是关于Python装饰器继承问题的主要内容,如果未能解决你的问题,请参考以下文章