【问题标题】:Best way to auto-update Python Class Attributes自动更新 Python 类属性的最佳方法
【发布时间】:2013-12-27 19:10:33
【问题描述】:

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

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

  • 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()

date_range 更改时,人们会推荐什么作为“更新投资组合Series 和基准Series”的最有效方法?

非常感谢,

-B

【问题讨论】:

    标签: python properties attributes pandas


    【解决方案1】:

    查看属性文档:

    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)

    【讨论】:

      【解决方案2】:

      这里有几件事让我觉得有点好笑……

      1. 如果你在 python2.x 上,你应该继承自 object,因为 properties are only documented to work with new style classes...
      2. 您的“属性”不应与您的某个属性同名...例如
      @财产 def 投资组合(自己): 返回 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]]
      

      【讨论】:

      • 所以在构造函数中,您是在分配:self.date_range = ((portfolio.index[0], portfolio.index[-1])) 还是在此处添加下划线?即self._date_range = ((portfolio.index[0], portfolio.index[-1]))
      • @Factor3 -- 可能使用下划线。但这取决于。当你想避免属性魔法时使用下划线,而当你希望属性魔法生效时不要使用下划线。
      猜你喜欢
      • 2014-04-17
      • 1970-01-01
      • 2021-05-17
      • 2020-11-26
      • 2014-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-11
      相关资源
      最近更新 更多