自动更新Python类属性的最佳方法

时间:2013-12-27 19:10:34

标签: python properties attributes pandas

我是一个类的菜鸟(主要是完成函数式编程),所以虽然我使用了一种特定的方法来实现以下功能,但是如果有一个“最佳实践”来实现我正在寻找的东西(除了属性和属性之外,我很乐意听到它。

我正在创建一个具有以下属性的投资组合对象:

  • date_range:相关时段的第一个和最后一个日期的元组
  • portfoliodate_range[0]:date_range[-1]
  • 之间的投资组合价值
  • benchmarkdate_range[0]:date_range[-1]
  • 之间的基准值

我想对这两个系列进行一些统计计算(仅供参考,Class定义可以在帖子的底部找到。)

import pandas
import numpy

#create the date range
In [1]: dt_rng = pandas.DatetimeIndex(start = '01/01/2000', freq = 'b', periods = 100)

#create the portfolio & benchmark series
In  [ 2]: p1 = 1000*numpy.exp(numpy.cumsum(numpy.random.randn(len(dt_rng),)/252.))
In  [ 3]: p2 = 1000*numpy.exp(numpy.cumsum(numpy.random.randn(len(dt_rng),)/252.))
In  [ 4]: port = pandas.Series(p1, dt_rng)
In  [ 5]: bench = pandas.Series(p2, dt_rng)

#create the Portfolio Object (Class Code at the bottom)
In  [ 6]: port_object = PortObj(port, bench)
In  [ 7]: port_object.port_return()
Out [10]: -0.00066236017291987359
In  [11]: port_object.bech_return()
Out [12]: -0.031054475224739697
In  [13]: port_object.alpha()
Out [14]: 0.030392115051819824
In  [15]: port_object.date_range
Out [16]: 
(Timestamp('2000-01-03 00:00:00', tz=None),
 Timestamp('2000-05-19 00:00:00', tz=None))

现在我想更改投资组合指标计算中使用的日期范围,所以假设我选择了dt_rng[15], dt_rng[60]的区间。所以:

In  [14]: port_object.date_range = ((dt_rng[15], dt_rng[50]))
Out [15]:
(Timestamp('2000-01-24 00:00:00', tz=None),
 Timestamp('2000-03-13 00:00:00', tz=None))

因此我们知道date_range已更改,但我的@set方法无法正常运行,可以通过(众多方法之一)看到:

In  [16:]: port_object.alpha()
Out [17:]: 0.030392115051819824 #same as above

我试图关注this post,但无法在班级内实施。

这是class:

class PortObj:
    def __init__(self, portfolio, benchmark):
        self.date_range = ((portfolio.index[0], portfolio.index[-1]))
        self.portfolio = portfolio
        self.benchmark = benchmark

    @property
    def date_range(self):
        return self.date_range
    @date_range.setter
    def date_range(self, date_range):
        self.date_range = date_range
        self.portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
        self.benchmark = self.benchmark.loc[date_range[0]:date_range[1]]

    @property
    def portfolio(self):
        return self.portfolio
    @portfolio.setter
    def portfolio(self, date_range):
        self.date_range = date_range
        self.portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
        self.benchmark = self.benchmark.loc[date_range[0]:date_range[1]]

    @property
    def benchmark(self):
        return self.benchmark
    @portfolio.setter
    def benchmark(self, date_range):
        self.date_range = date_range
        self.portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
        self.benchmark = self.benchmark.loc[date_range[0]:date_range[1]]

    def port_return(self):
        return numpy.divide(self.portfolio[-1], self.portfolio[0]) - 1

    def bench_return(self):
        return numpy.divide(self.benchmark[-1], self.benchmark[0]) - 1

    def alpha(self):
        return self.port_return() - self.bench_return()

Series发生变化时,人们会建议什么是“更新投资组合Series和基准date_range”的最有效方式?

非常感谢,

-B

2 个答案:

答案 0 :(得分:3)

查看属性文档:

http://docs.python.org/2/library/functions.html#property

特别是示例:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

您需要将值存储在单独的“私有”变量中,而不是在getter / setter中使用相同的属性名称。通常,这会给你一个递归错误,但由于你不是从对象继承,你使用的是旧式类,它与描述符无法正常工作。因此,无论如何,构造函数的self.date_range = ...赋值都会替换您的属性。

简而言之,您需要:

  • 继承自(对象)以使属性正常工作。
  • 使用名称与您的属性不同的变量(例如self._date_range)

答案 1 :(得分:0)

这里有一些让我觉得有点滑稽的东西......

  1. 如果您使用的是python2.x,那么您应该从properties are only documented to work with new style classes以后继承object ...
  2. 你不应该拥有与你的某个特征同名的“属性”......例如
  3.     @property
        def portfolio(self):
            return self.portfolio
    

    我真的很惊讶这样的事情并没有给你一个递归错误...通常,对于像这样的东西,你包装一个不同的(非属性)属性:

    @property
    def portfolio(self):
        return self._portfolio  # Note the leading underscore.
    
    @portfolio.setter
    def portfolio(self, date_range):
        self._date_range = date_range
        self._portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
        self._benchmark = self.benchmark.loc[date_range[0]:date_range[1]]