【问题标题】:Can I write this as a wrapper?我可以把它写成包装器吗?
【发布时间】:2019-03-24 15:36:05
【问题描述】:

我有以下代码:

import numpy as np

class Basis(object):

def __init__(self, dimension):
    self.dimension = dimension

def coord(self, c):
    if self.dimension <= 2:
        return c
    else:
        return c + [0]*(self.dimension-2)

@property
def zerocoord(self):
    return self.coord([0,0])                  

@property
def Dcoord(self):
    return self.coord([1,0])

@property
def Tcoord(self):
    return self.coord([0,1])

@property
def Xcoord(self):
    return self.coord([1./np.sqrt(2), 1./np.sqrt(2)])

@property
def Ycoord(self):
    return self.coord([-1./np.sqrt(2), 1./np.sqrt(2)])

这里所有的属性基本上每个属性都在调用相同的方法coord。这是因为我提供的实际数组 coord[0,0], [1,0], [0,1] 等是固定的,但可能会根据实例属性 dimension 进行扩展。

我对 Python 有点陌生,但直觉上(也许是天真地)我认为这可以写成一个包装器......所以类似于:

@property
def coord(self)

@coord
def Dcoord(self)

这会使代码更优雅。

有人可以帮帮我吗?

【问题讨论】:

  • 您想将property 别名为coord?只需在您的类定义之前添加coord = property。否则,这是没有意义的,因为这些函数中的每一个都获得了与 self.coord 不同的东西,所以你不能有一个通用的包装器。

标签: python wrapper python-decorators


【解决方案1】:

定义您自己的描述符Coord,而不是使用property

from __future__ import division
import numpy as np

class Coord(object):
    def __init__(self, p1, p2):
        self.foo = [p1, p2]

    def __get__(self, obj, type=None):
        if obj.dimension > 2:
            return self.foo + [0 for x in range(2, obj.dimension)]
        else:
            return self.foo


class Basis(object):
    def __init__(self, d):
        self.dimension = d

    zerocoord = Coord(0, 0)
    dcoord = Coord(1, 0)
    tcoord = Coord(0, 1)
    xcoord = Coord(1/np.sqrt(2), 1/np.sqrt(2))
    ycoord = Coord(-1/np.sqrt(2), -1/np.sqrt(2))

现在,确定每种坐标形状的逻辑嵌入在描述符本身中,而不是您的类中。

一些例子:

>>> Basis(1).dcoord
[1, 0]
>>> Basis(3).dcoord
[1, 0, 0]
>>> Basis(4).tcoord
[0, 1, 0, 0]

【讨论】:

    【解决方案2】:

    您可以将要传递给coord 方法的属性名称和它们各自的常量值放在一个元组序列中,然后使用循环来相应地设置属性:

    class Basis(object):
    
        def __init__(self, dimension):
            self.dimension = dimension
    
        def coord(self, c):
            if self.dimension <= 2:
                return c
            else:
                return c + [0]*(self.dimension-2)
    
    for name, value in ('zerocoord', [0, 0]), ('Dcoord', [1, 0]), ('Tcoord', [0, 1]), ('Xcoord', [1./np.sqrt(2), 1./np.sqrt(2)]), ('Ycoord', [-1./np.sqrt(2), 1./np.sqrt(2)]):
        setattr(Basis, name, property(lambda self, value=value: self.coord(value)))
    

    【讨论】:

      【解决方案3】:

      您可以使用包装这些方法的装饰器,方法是为它们调用coord 方法并将它们转换为属性,这样这些方法只需要返回相关的常量即可:

      def coord_property(func):
          def wrapper(self):
              return self.coord(func(self))
          return property(wrapper)
      
      class Basis(object):
      
          def __init__(self, dimension):
              self.dimension = dimension
      
          def coord(self, c):
              if self.dimension <= 2:
                  return c
              else:
                  return c + [0]*(self.dimension-2)
      
          @coord_property
          def zerocoord(self):
              return [0,0]                  
      
          @coord_property
          def Dcoord(self):
              return [1,0]
      
          @coord_property
          def Tcoord(self):
              return [0,1]
      
          @coord_property
          def Xcoord(self):
              return [1./np.sqrt(2), 1./np.sqrt(2)]
      
          @coord_property
          def Ycoord(self):
              return [-1./np.sqrt(2), 1./np.sqrt(2)]
      

      【讨论】:

        【解决方案4】:

        您不能将值传递给属性 getter,装饰器会很快变得笨拙。如果您至少使用 3.4,则可以使用 functools.partialmethod 减少行数。

        但是,最好保持代码原样,因为“显式优于隐式”。

        from functools import partialmethod
        
        class BasisWrapped(object):
        
            def __init__(self, dimension):
                self.dimension = dimension
        
            def coord(self, c):
                if self.dimension <= 2:
                    return c
                else:
                    return c + [0]*(self.dimension-2)
        
            zerocoord = partialmethod(coord, [0, 0])
            d_coord = partialmethod(coord, [1, 0])
            t_coord = partialmethod(coord, [0, 1])
            x_coord = partialmethod(coord, [1./np.sqrt(2), 1./np.sqrt(2)])
            y_coord = partialmethod(coord, [-1./np.sqrt(2), 1./np.sqrt(2)])
        

        【讨论】:

          【解决方案5】:

          您可以通过这种方式摆脱大量样板代码:

          import numpy as np
          
          class Basis(object):
          
              def __init__(self, dimension):
                  self.dimension = dimension
          
              def coord(self, c):
                  return c if self.dimension <= 2 else (c + [0]*(self.dimension-2))
          
              def _coord_prop(loc):
                  @property
                  def prop(self):
                      return self.coord(loc)
                  return prop
          
              zerocoord = _coord_prop([0, 0])
              Dcoord = _coord_prop([1, 0])
              Tcoord = _coord_prop([0, 1])
              Xcoord = _coord_prop([1./np.sqrt(2), 1./np.sqrt(2)])
              Ycoord = _coord_prop([-1./np.sqrt(2), 1./np.sqrt(2)])
          
              del _coord_prop  # Only used inside class definition.
          
          basis = Basis(2)
          print(basis.zerocoord)  # -> [0, 0]
          print(basis.Dcoord)     # -> [1, 0]
          print(basis.Tcoord)     # -> [0, 1]
          print(basis.Xcoord)     # -> [0.7071067811865475, 0.7071067811865475]
          print(basis.Ycoord)     # -> [-0.7071067811865475, 0.7071067811865475]
          

          【讨论】:

            【解决方案6】:

            就个人而言,我认为代码已经相当优雅了。 您不应该/不能将coord 设为属性,因为:

            • 您将无法向它传递参数,因为属性是(计算的?)字段的“getter”。 coord 表现为一个函数,因此应该是一个。

            如果您真的只是想减少代码的大小,您可以尝试其他答案中的一种方法,尽管我认为这并不是真正必要的。


            旁白:我从您的用例中了解到,您希望用户能够使用他们选择的自定义坐标调用coord?如果没有,您可以考虑通过将其重命名为 _coord 将其设为私有。

            【讨论】:

              猜你喜欢
              • 2018-09-17
              • 1970-01-01
              • 2015-04-24
              • 1970-01-01
              • 2013-06-12
              • 2014-03-18
              • 1970-01-01
              • 2015-09-04
              • 2019-03-24
              相关资源
              最近更新 更多