将函数或属性应用于同一类的多个方法
Posted
技术标签:
【中文标题】将函数或属性应用于同一类的多个方法【英文标题】:Apply function or property to multiple methods of same class 【发布时间】:2021-11-28 11:25:30 【问题描述】:在以下示例中,MyExample
类读取文件名中包含下划线的文件名列表。
class MyExample(object):
def __init__(self, location: str):
self.location = location
def get_file_names(self):
# For simplicity, lets just return an example of the output
return ["my_file_name_01.txt", "my_file_name_02.txt"]
def get_file_name_number(self):
# Idem as before.
return ["file_name_01.txt", "file_name_02.txt"]
def get_file_size(self):
# It does not return a list of strings so the desired
# property or function will not be applied here
return [3800, 4000]
代码执行时,结果是
my_object = MyExample("./a/random/path")
print(my_object.get_file_names())
print(my_object.get_file_name_number())
print(my_object.get_file_size())
# The results:
['my_file_name_01.txt', 'my_file_name_02.txt']
['file_name_01.txt', 'file_name_02.txt']
[3800, 4000]
现在,我想找到一种应用函数或属性(我们称之为to_dot
)的方法,它可以替换get_file_names()
和@987654326 输出中的点的下划线@。
最终代码应该返回类似
# Calling this function with .to_dots
my_object.get_file_names().to_dots
["my.file.name.01.txt", "my.file.name.02.txt"] # <-- The desired output
# Calling this function with .to_dots
my_object.get_file_name_number().to_dots
["file.name.01.txt", "file.name.02.txt"] # <-- The desired output
# Calling this function with .to_dots
my_object.get_file_name_number().to_dots
AttributeError # # <-- The desired output ... or something similar
有没有办法将to_dots
添加到MyExample
类中,以替换里面某些方法的下划线?
我对装饰器不是很熟悉,但我怀疑可能有一个技巧可以做到这一点。到目前为止,我没有成功尝试使用@property
,但据我所知,有很多类型的装饰器......或者也许它可以在没有装饰器的情况下完成,我很迷茫。
谢谢。
【问题讨论】:
您的问题是如何编写一个用点替换下划线的函数,或者如何将任何函数应用于列表中的所有项目,或者如何编写装饰器来转换方法的返回值有什么功能吗? @mkrieger1。我知道使用str.replace
我可以将 _
更改为 .
并且使用理解列表(或许多其他方式)我可以将替换应用于所有项目。我的问题是如何向类中的某些方法添加函数。谢谢。
我会让to_dots
成为一个接受列表的独立函数。然后像这样调用它:to_dots(my_object.get_file_names())
这更灵活,因为现在您可以在任何您想要的列表对象上使用它。不仅仅是这个类返回的。
【参考方案1】:
你可以这样做:
from collections import UserList
class MyList(UserList):
@property
def to_dots(self):
return [s.replace("_", ".") for s in self.data]
class MyExample(object):
def __init__(self, location: str):
self.location = location
def get_file_names(self):
return MyList(["my_file_name_01.txt", "my_file_name_02.txt"])
结果
my_object = MyExample("./a/random/path")
print(my_object.get_file_names())
print(my_object.get_file_names().to_dots)
是
['my_file_name_01.txt', 'my_file_name_02.txt']
['my.file.name.01.txt', 'my.file.name.02.txt']
【讨论】:
谢谢@Timus。我已经接受这个答案是正确的,因为这个答案与帖子的原始问题更相关,但是,在考虑了两次之后,根据您对列表功能的评论,我不会在我的代码中使用这种方法。我意识到避免使用诸如 MyList 之类的类可能是一种更好的做法,因为它不会返回真正的列表。也许这是一个值得商榷的观点,但我认为更好的做法(或更 Pythonic)是使用 trincot 建议的方法,这并不意味着创建更多类。 @eliasmaxil 我同意,我可能也不会使用这种方法 - 取决于更广泛的背景。我应该在答案中明确表示,这只是为了展示您如何在更狭义的意义上实现您所要求的(绝不是一个好的回答方式)。无论如何,有趣的问题!【参考方案2】:您可以使用类装饰器将函数装饰器应用于指定的类方法。
from functools import wraps
import inspect
def dec_methods(decorator, *members):
"""Class decorator to apply specfied decorator to specified members of class."""
def dec_the_class(cls):
for name, m in inspect.getmembers(cls, inspect.isfunction):
if name in members:
setattr(cls, name, decorator(m))
return cls
return dec_the_class
def to_dots(func):
"""Function decorator to replace '_' with dots in list of strings returned."""
@wraps(func)
def wrapped(*args, **kwargs):
results = func(*args, **kwargs)
results = [result.replace('_', '.') for result in results]
return results
return wrapped
@dec_methods(to_dots, 'get_file_names', 'get_file_name_number')
class MyExample(object):
def __init__(self, location: str):
self.location = location
def get_file_names(self):
# For simplicity, lets just return an example of the output
return ["my_file_name_01.txt", "my_file_name_02.txt"]
def get_file_name_number(self):
# Idem as before.
return ["file_name_01.txt", "file_name_02.txt"]
def get_file_size(self):
# It does not return a list of strings so the desired
# property or function will not be applied here
return [3800, 4000]
my_object = MyExample("./a/random/path")
print(my_object.get_file_names()) # -> ['my.file.name.01.txt', 'my.file.name.02.txt']
print(my_object.get_file_name_number()) # -> ['file.name.01.txt', 'file.name.02.txt']
print(my_object.get_file_size()) # -> [3800, 4000]
【讨论】:
谢谢@martineau,这个答案也有效。我刚刚投票给了另一个需要更少代码来实现相同结果的人。 它更短,因为它在做不同的事情。在我的回答中,类的方法经过修饰,因此它们会自动执行您想要的操作 - 即您不需要像在另一个答案中那样自己手动调用to_dots()
的结果。另一个答案也是改变方法返回的值的类型(尽管它是list
的子类)。
是的,你是对的,代码传递的是列表而不是列表的子类,这有助于避免代码其他部分的错误。【参考方案3】:
为什么不将所需的输出格式(点或不点)作为可选参数传递给这些方法?
class MyExample(object):
def __init__(self, location: str):
self.location = location
@staticmethod
def map_list(lst, dotted=False):
return [s.replace("_", ".") for s in lst] if dotted else lst
def get_file_names(self, dotted=False):
return self.map_list(["my_file_name_01.txt", "my_file_name_02.txt"], dotted)
def get_file_name_number(self, dotted=False):
return self.map_list(["file_name_01.txt", "file_name_02.txt"], dotted)
def get_file_size(self):
return [3800, 4000]
my_object = MyExample("./a/random/path")
print(my_object.get_file_names()) # Underscores untouched
print(my_object.get_file_name_number(dotted=True)) # Underscores replaced
print(my_object.get_file_size())
【讨论】:
谢谢@trincot。我必须承认这是最简单的解决方案,我明确地在代码中使用它。我喜欢它,因为这样我将避免创建更多的类并进行更多的导入,此外它只会在原始代码中添加几行。但是,由于最初的答案是如何将函数应用于类的不同方法,所以我必须投票给与原始问题更相关的答案。但是对于这个答案的其他读者,我建议给这个答案更多的投票。【参考方案4】:定义你自己的装饰器:
def to_dots(func):
def inner():
original_return = func()
return [item.replace('_','.') for item in original_return ]
return inner
并将其用作:
@to_dots
def get_file_names(self):
【讨论】:
以上是关于将函数或属性应用于同一类的多个方法的主要内容,如果未能解决你的问题,请参考以下文章
在 Interface Builder 中实例化同一类的多个对象会导致共享属性