Python-像扩展函数一样扩展属性 [英] Python - extending properties like you'd extend a function
问题描述
如何扩展python属性?
子类可以通过在重载版本中调用超类的功能,然后对结果进行操作来扩展超类的功能.这是当我说扩展功能"时的意思的示例:
A subclass can extend a super class's function by calling it in the overloaded version, and then operating on the result. Here's an example of what I mean when I say "extending a function":
# Extending a function (a tongue-in-cheek example)
class NormalMath(object):
def __init__(self, number):
self.number = number
def add_pi(self):
n = self.number
return n + 3.1415
class NewMath(object):
def add_pi(self):
# NewMath doesn't know how NormalMath added pi (and shouldn't need to).
# It just uses the result.
n = NormalMath.add_pi(self)
# In NewMath, fractions are considered too hard for our users.
# We therefore silently convert them to integers.
return int(n)
是否有与扩展函数类似的操作,但是对于使用属性装饰器的函数而言?
我想在获得昂贵的计算属性后立即进行一些额外的计算.我需要保持属性的访问延迟.我不希望用户必须调用特殊的例程来进行计算.基本上,我不希望用户一开始就知道这些计算.但是,该属性必须保留为属性,因为我有需要支持的旧代码.
I want to do some additional calculations immediately after getting an expensive-to-compute attribute. I need to keep the attribute's access lazy. I don't want the user to have to invoke a special routine to make the calculations. basically, I don't want the user to ever know the calculations were made in the first place. However, the attribute must remain a property, since i've got legacy code I need to support.
也许这是装饰工的工作?如果我没记错的话,decorator是一个包装另一个函数的函数,我正在寻找一个包含更多计算的属性,然后再次将其呈现为一个属性,这似乎是一个相似的主意……但是我不太清楚.
Maybe this is a job for decorators? If I'm not mistaken, decorator is a function that wraps another function, and I'm looking to wrap a property with some more calculations, and then present it as a property again, which seems like a similar idea... but I can't quite figure it out.
我有一个 LogFile 的基类,它具有构造成本很高的属性 .dataframe .我已经将它实现为一个属性(使用属性装饰器),因此在我请求数据帧之前,它实际上不会解析日志文件.到目前为止,效果很好.我可以构造一堆(超过100个)LogFile对象,并使用便宜的方法来过滤和选择仅要解析的重要对象.每当我一次又一次使用相同的LogFile时,仅在第一次访问数据框时才需要解析它.
I've got a base class LogFile with an expensive-to-construct attribute .dataframe. I've implemented it as a property (with the property decorator), so it won't actually parse the log file until I ask for the dataframe. So far, it works great. I can construct a bunch (100+) LogFile objects, and use cheaper methods to filter and select only the important ones to parse. And whenever I'm using the same LogFile over and over, i only have to parse it the first time I access the dataframe.
现在,我需要编写一个LogFile子类 SensorLog ,该子类将一些额外的列添加到基类的dataframe属性中,但是我不太清楚调用超类的dataframe构造的语法例程(不了解其内部工作原理),然后对生成的数据帧进行操作,然后然后缓存/返回它.
Now I need to write a LogFile subclass, SensorLog, that adds some extra columns to the base class's dataframe attribute, but I can't quite figure out the syntax to call the super class's dataframe construction routines (without knowing anything about their internal workings), then operate on the resulting dataframe, and then cache/return it.
# Base Class - rules for parsing/interacting with data.
class LogFile(object):
def __init__(self, file_name):
# file name to find the log file
self.file_name = file_name
# non-public variable to cache results of parse()
self._dataframe = None
def parse(self):
with open(self.file_name) as infile:
...
...
# Complex rules to interpret the file
...
...
self._dataframe = pandas.DataFrame(stuff)
@property
def dataframe(self):
"""
Returns the dataframe; parses file if necessary. This works great!
"""
if self._dataframe is None:
self.parse()
return self._dataframe
@dataframe.setter
def dataframe(self,value):
self._dataframe = value
# Sub class - adds more information to data, but does't parse
# must preserve established .dataframe interface
class SensorLog(LogFile):
def __init__(self, file_name):
# Call the super's constructor
LogFile.__init__(self, file_name)
# SensorLog doesn't actually know about (and doesn't rely on) the ._dataframe cache, so it overrides it just in case.
self._dataframe = None
# THIS IS THE PART I CAN'T FIGURE OUT
# Here's my best guess, but it doesn't quite work:
@property
def dataframe(self):
# use parent class's getter, invoking the hidden parse function and any other operations LogFile might do.
self._dataframe = LogFile.dataframe.getter()
# Add additional calculated columns
self._dataframe['extra_stuff'] = 'hello world!'
return self._dataframe
@dataframe.setter
def dataframe(self, value):
self._dataframe = value
现在,在交互式会话中使用这些类时,用户应该能够以相同的方式进行交互.
Now, when these classes are used in an interactive session, the user should be able to interact with either in the same way.
>>> log = LogFile('data.csv')
>>> print log.dataframe
#### DataFrame with 10 columns goes here ####
>>> sensor = SensorLog('data.csv')
>>> print sensor.dataframe
#### DataFrame with 11 columns goes here ####
我有很多现有代码,它们采用一个 LogFile 实例,该实例提供一个 .dataframe 属性,并执行一些有趣的操作(大部分是绘图).我希望 SensorLog 实例具有相同的界面,以便它们可以使用相同的代码.是否可以扩展超类的数据帧获取器以利用现有例程?如何?还是我最好以不同的方式来做?
I have lots of existing code that takes a LogFile instance which provides a .dataframe attribute and dos something interesting (mostly plotting). I would LOVE to have SensorLog instances present the same interface so they can use the same code. Is it possible to extend the super-class's dataframe getter to take advantage of existing routines? How? Or am I better off doing this a different way?
感谢您阅读那堵巨大的文字墙.亲爱的读者,您是互联网超级英雄.有任何想法吗?
Thanks for reading that huge wall of text. You are an internet super hero, dear reader. Got any ideas?
推荐答案
您应该调用超类属性,而不是通过self._dataframe
绕过它们.这是一个通用示例:
You should be calling the superclass properties, not bypassing them via self._dataframe
. Here's a generic example:
class A(object):
def __init__(self):
self.__prop = None
@property
def prop(self):
return self.__prop
@prop.setter
def prop(self, value):
self.__prop = value
class B(A):
def __init__(self):
super(B, self).__init__()
@property
def prop(self):
value = A.prop.fget(self)
value['extra'] = 'stuff'
return value
@prop.setter
def prop(self, value):
A.prop.fset(self, value)
并使用它:
b = B()
b.prop = dict((('a', 1), ('b', 2)))
print(b.prop)
输出:
{'a': 1, 'b': 2, 'extra': 'stuff'}
我通常建议将副作用放置在setter而不是getter中,例如:
I would generally recommend placing side-effects in setters instead of getters, like this:
class A(object):
def __init__(self):
self.__prop = None
@property
def prop(self):
return self.__prop
@prop.setter
def prop(self, value):
self.__prop = value
class B(A):
def __init__(self):
super(B, self).__init__()
@property
def prop(self):
return A.prop.fget(self)
@prop.setter
def prop(self, value):
value['extra'] = 'stuff'
A.prop.fset(self, value)
通常也要避免在getter中进行昂贵的操作(例如您的parse方法).
Having costly operations within a getter is also generally to be avoided (such as your parse method).
这篇关于Python-像扩展函数一样扩展属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!