Python-像扩展函数一样扩展属性 [英] Python - extending properties like you'd extend a function

查看:62
本文介绍了Python-像扩展函数一样扩展属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何扩展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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆