Python像扩展函数一样扩展属性

2024-09-30 01:36:57 发布

您现在位置:Python中文网/ 问答频道 /正文

问题

如何扩展python属性?

子类可以通过在重载版本中调用超级类的函数,然后对结果进行操作来扩展它。下面是我所说的“扩展函数”的一个例子:

# 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)

对于使用属性decorator的函数,是否存在类似于扩展函数的操作?

我想在得到一个昂贵的计算属性后立即进行一些额外的计算。我需要保持属性的访问延迟。我不希望用户必须调用一个特殊的例程来进行计算。基本上,我不想让用户知道计算是在第一时间进行的。但是,属性必须保持为属性,因为我有需要支持的遗留代码。在

也许这是装修工的工作?如果我没搞错的话,decorator是一个包装另一个函数的函数,我希望用更多的计算来包装一个属性,然后再次将它作为一个属性来呈现,这似乎是一个类似的想法。。。但我不太明白。在

我的具体问题

我有一个基类LogFile,它有一个构造属性的昂贵的.dataframe。我已经将它实现为一个属性(使用property decorator),因此在我请求dataframe之前,它不会实际解析日志文件。到目前为止,效果很好。我可以构造一堆(100多个)LogFile对象,并使用更便宜的方法过滤和选择要解析的重要对象。每当我反复使用同一个日志文件时,我只需要在第一次访问数据帧时解析它。在

现在我需要编写一个LogFile子类,SensorLog,它在基类的dataframe属性中添加了一些额外的列,但是我不能很好地理解调用超级类的dataframe构造例程的语法(不知道它们的内部工作原理),然后对生成的数据帧进行操作,然后然后缓存/返回它。在

^{pr2}$

现在,当在交互式会话中使用这些类时,用户应该能够以相同的方式与它们进行交互。在

>>> 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实例提供相同的接口,以便它们可以使用相同的代码。是否可以扩展超级类的dataframe getter来利用现有的例程?怎样?或者我应该换一种方式?在

感谢你阅读那堵巨大的文字墙。亲爱的读者,你是个网络超级英雄。有什么想法吗?在


Tags: 函数代码用户selfaddnumberdataframe属性
3条回答

如果我理解正确,您要做的是从子实例调用父级的方法。通常的方法是使用super内置的。在

我以您的言不由衷的例子为例,将其修改为使用super,以便向您展示:

class NormalMath(object):
    def __init__(self, number):
        self.number = number

    def add_pi(self):
        n = self.number
        return n + 3.1415


class NewMath(NormalMath):
    def add_pi(self):
        # this will call NormalMath's add_pi with
        normal_maths_pi_plus_num = super(NewMath, self).add_pi()
        return int(normal_maths_pi_plus_num)

在日志示例中,不要调用:

^{pr2}$

你应该打电话给:

self._dataframe = super(SensorLog, self).dataframe

您可以阅读更多关于superhere

编辑:即使我给你的例子处理的是方法,对@properties做同样的操作也不成问题。在

你需要考虑一些可能性:

1/从logfile继承并重写派生传感器类中的parse。应该可以修改对dataframe起作用的方法,使其不管dataframe有多少成员都可以工作——因为您使用的是pandas很多都是为您完成的。在

2/使sensor成为logfile的实例,然后提供它自己的解析方法。在

3/泛化parse,可能还有其他一些方法,可以使用数据描述符列表,也可以是在类初始化器中设置的方法/规则字典,或者由方法设置。在

4/如果你和其他人认为pandas可以被接受为有用的扩展,那么你可以考虑更多地使用pandas中已经存在的方法,或者可能扩展pandas来提供缺失的方法。在

我个人认为你会发现选项3或4的好处是最强大的。在

您应该调用超类属性,而不是通过self._dataframe绕过它们。下面是一个通用示例:

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)

使用它:

^{pr2}$

输出:

{'a': 1, 'b': 2, 'extra': 'stuff'}

我通常建议把副作用放在setters而不是getter中,比如:

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方法)。在

相关问题 更多 >

    热门问题