Python 使用@property与getter和setter

Python 使用@property与getter和setter,python,properties,getter-setter,Python,Properties,Getter Setter,下面是一个纯Python特定的设计问题: class MyClass(object): ... def get_my_attr(self): ... def set_my_attr(self, value): ... 及 Python允许我们以任何一种方式进行操作。如果你要设计一个Python程序,你会使用哪种方法?为什么?简单的回答是:属性可以轻松获胜。总是 有时需要有能手和二传手,但即便如此,我还是会把他们“隐藏”在外面。在Pyth

下面是一个纯Python特定的设计问题:

class MyClass(object):
    ...
    def get_my_attr(self):
        ...

    def set_my_attr(self, value):
        ...


Python允许我们以任何一种方式进行操作。如果你要设计一个Python程序,你会使用哪种方法?为什么?

简单的回答是:属性可以轻松获胜。总是

有时需要有能手和二传手,但即便如此,我还是会把他们“隐藏”在外面。在Python中有很多方法可以做到这一点(
getattr
setattr
\uuuu getattribute\uuuu
,等等),但一个非常简洁明了的方法是:

def set_email(self, value):
    if '@' not in value:
        raise Exception("This doesn't look like an email address.")
    self._email = value

def get_email(self):
    return self._email

email = property(get_email, set_email)

这介绍了Python中的getter和setter主题。

对我来说,使用属性更直观,更适合大多数代码

比较

o.x = 5
ox = o.x
vs


对我来说很明显,这更容易阅读。属性允许私有变量更容易。

使用属性可以让你从正常的属性访问开始,然后再使用属性。

我觉得属性是让你只在实际需要时才编写getter和setter的开销

Java编程文化强烈建议永远不要授予属性访问权,而要通过getter和setter,只使用那些实际需要的属性。
总是编写这些显而易见的代码片段有点冗长,注意到70%的时间它们从未被一些非平凡的逻辑所取代

在Python中,人们实际上关心这种开销,因此您可以采用以下做法:

  • 开始时不要使用getter和setter,如果不需要的话
  • 使用
    @property
    在不更改其余代码语法的情况下实现它们
    • 更喜欢酒店。这就是他们在那里的目的

      原因是所有属性在Python中都是公共的。以下划线或两个下划线开头的名称只是一个警告,即给定的属性是一个实现细节,在未来的代码版本中可能不会保持不变。这不会阻止您实际获取或设置该属性。因此,标准属性访问是最重要的mal,python式的访问属性的方式


      属性的优点是,它们在语法上与属性访问相同,因此您可以在不更改客户端代码的情况下从一个属性更改为另一个属性。您甚至可以有一个使用属性的类版本(例如,对于通过协定或调试的代码)同时,您不必为所有内容编写getter和setter,以防以后需要更好地控制访问。

      在Python中,您使用getter或setter或properties不仅仅是为了好玩。您首先使用属性,然后使用只有在需要时,才最终迁移到属性,而不必使用类更改代码

      确实有很多扩展名为.py.使用GETTER和SETTER和继承和无意义类的地方,例如简单元组会做的,但是它是用Python编写C++或java的人编写的代码。


      这不是Python代码。

      我认为两者都有各自的位置。使用
      @property
      的一个问题是,很难使用标准类机制扩展子类中getter或setter的行为。问题是实际的getter/setter函数隐藏在属性中

      您实际上可以掌握这些功能,例如使用

      class C(object):
          _p = 1
          @property
          def p(self):
              return self._p
          @p.setter
          def p(self, val):
              self._p = val
      
      您可以以
      C.p.fget
      C.p.fset
      的形式访问getter和setter函数,但是您不能轻易地使用常规方法继承(例如super)功能来扩展它们。在深入研究super的复杂之处之后,您确实可以通过以下方式使用super:

      # Using super():
      class D(C):
          # Cannot use super(D,D) here to define the property
          # since D is not yet defined in this scope.
          @property
          def p(self):
              return super(D,D).p.fget(self)
      
          @p.setter
          def p(self, val):
              print 'Implement extra functionality here for D'
              super(D,D).p.fset(self, val)
      
      # Using a direct reference to C
      class E(C):
          p = C.p
      
          @p.setter
          def p(self, val):
              print 'Implement extra functionality here for E'
              C.p.fset(self, val)
      

      但是,使用super()非常笨拙,因为必须重新定义属性,并且必须使用稍微违反直觉的super(cls,cls)获取p的未绑定副本的机制。

      在大多数情况下,我都不希望使用这两种方法。属性的问题是它们会降低类的透明度。特别是,如果您要从setter引发异常,这是一个问题。例如,如果您有Account.email属性:

      class Account(object):
          @property
          def email(self):
              return self._email
      
          @email.setter
          def email(self, value):
              if '@' not in value:
                  raise ValueError('Invalid email address.')
              self._email = value
      
      然后,类的用户不希望为属性赋值会导致异常:

      a = Account()
      a.email = 'badaddress'
      --> ValueError: Invalid email address.
      
      因此,异常可能未经处理,或者在调用链中传播得太高而无法正确处理,或者导致向程序用户提供一个非常没有帮助的回溯(遗憾的是,这在python和java世界中太常见了)

      我还避免使用getter和setter:

      • 因为预先为所有属性定义它们非常耗时
      • 不必要地延长了代码的数量,这使得理解和维护代码更加困难
      • 如果仅根据需要为属性定义它们,则类的接口将发生更改,从而损害类的所有用户
      与属性和getter/setter不同,我更喜欢在定义良好的位置执行复杂逻辑,例如在验证方法中:

      class Account(object):
          ...
          def validate(self):
              if '@' not in self.email:
                  raise ValueError('Invalid email address.')
      
      或类似的帐户。保存方法


      请注意,我并不是想说没有属性有用的情况,只是如果您可以使类变得足够简单和透明,而不需要它们,那么您的情况可能会更好。

      [TL;DR?您可以跳到代码示例的末尾。
      ]

      事实上,我更喜欢使用不同的习惯用法,这对于一次性使用来说有点复杂,但是如果您有更复杂的用例,这就很好了

      先来点背景知识

      属性很有用,因为它们允许我们以编程的方式处理设置和获取值,但仍然允许将属性作为属性访问。我们可以将“get”转换为“computations”(本质上),也可以将“set”转换为“events”。因此,假设我们有以下类
      a = Account()
      a.email = 'badaddress'
      --> ValueError: Invalid email address.
      
      class Account(object):
          ...
          def validate(self):
              if '@' not in self.email:
                  raise ValueError('Invalid email address.')
      
      class Example(object):
          def __init__(self, x=None, y=None):
              self.x = x
              self.y = y
      
          def getX(self):
              return self.x or self.defaultX()
      
          def getY(self):
              return self.y or self.defaultY()
      
          def setX(self, x):
              self.x = x
      
          def setY(self, y):
              self.y = y
      
          def defaultX(self):
              return someDefaultComputationForX()
      
          def defaultY(self):
              return someDefaultComputationForY()
      
      class Example(object):
          def __init__(self, x=None, y=None):
              self._x = x
              self._y = y
      
          @property
          def x(self):
              return self.x or self.defaultX()
      
          @x.setter
          def x(self, value):
              self._x = value
      
          @property
          def y(self):
              return self.y or self.defaultY()
      
          @y.setter
          def y(self, value):
              self._y = value
      
          # default{XY} as before.
      
      e.x[a,b,c] = 10
      e.x[d,e,f] = 20
      
      e.x = [a,b,c,10]
      e.x = [d,e,f,30]
      
      def x(self, *args):
          return defaultX()
      
      print e.x     -> The default at time T0
      e.x = 1
      print e.x     -> 1
      e.x = None
      print e.x     -> The default at time T1
      
      class UberProperty(object):
      
          def __init__(self, method):
              self.method = method
              self.value = None
              self.isSet = False
      
          def setValue(self, value):
              self.value = value
              self.isSet = True
      
          def clearValue(self):
              self.value = None
              self.isSet = False
      
      class Example(object):
      
          @uberProperty
          def x(self):
              return defaultX()
      
      def uberProperty(f):
          return UberProperty(f)
      
      class UberProperty(object):
      
          def __init__(self, method):
              self.method = method
      
      class Example(object):
      
          @uberProperty
          def x(self):
              return defaultX()
      
      print Example.x     <__main__.UberProperty object at 0x10e1fb8d0>
      print Example().x   <__main__.UberProperty object at 0x10e1fb8d0>
      
      class BoundUberProperty(object):
          def __init__(self, obj, uberProperty):
              self.obj = obj
              self.uberProperty = uberProperty
              self.isSet = False
      
          def setValue(self, value):
              self.value = value
              self.isSet = True
      
          def getValue(self):
              return self.value if self.isSet else self.uberProperty.method(self.obj)
      
          def clearValue(self):
              del self.value
              self.isSet = False
      
      class UberObject(object):
          def __init__(self):
              for k in dir(self):
                  v = getattr(self, k)
                  if isinstance(v, UberProperty):
                      v = BoundUberProperty(self, v)
                      setattr(self, k, v)
      
      e = Example()
      print e.x               -> <__main__.BoundUberProperty object at 0x104604c90>
      
      @uberProperty
      def x(self):
          return *datetime.datetime.now()*
      
      print e.x.getValue()
      print e.x.getValue()
      e.x.setValue(datetime.date(2013, 5, 31))
      print e.x.getValue()
      e.x.clearValue()
      print e.x.getValue()
      
        class Example(object):
            @uberProperty
            def x(self):
                ...
      
            y = x
      
      import datetime
      
      class UberObject(object):
          def uberSetter(self, value):
              print 'setting'
      
          def uberGetter(self):
              return self
      
          def __init__(self):
              for k in dir(self):
                  v = getattr(self, k)
                  if isinstance(v, UberProperty):
                      v = BoundUberProperty(self, v)
                      setattr(self, k, v)
      
      
      class UberProperty(object):
          def __init__(self, method):
              self.method = method
      
      class BoundUberProperty(object):
          def __init__(self, obj, uberProperty):
              self.obj = obj
              self.uberProperty = uberProperty
              self.isSet = False
      
          def setValue(self, value):
              self.value = value
              self.isSet = True
      
          def getValue(self):
              return self.value if self.isSet else self.uberProperty.method(self.obj)
      
          def clearValue(self):
              del self.value
              self.isSet = False
      
          def uberProperty(f):
              return UberProperty(f)
      
      class Example(UberObject):
      
          @uberProperty
          def x(self):
              return datetime.datetime.now()
      
      class MyClass(object):
      ...        
      @property
      def my_attr(self):
          ...
      
      def set_my_attr(self, value):
          ...
      
      my_object.my_atttr = 4.
      
      def slow_function(my_object):
          my_object.my_attr = 4.
          my_object.do_something()
      
      def Foo:
          def set_num(self, num, force=False):
              ...
      
      class Foo:
          def __init__(self):
              self.__num = 0
      
          @property
          def num(self):
              return self.__num
      
          @num.setter
          def num(self, num):
              self.__num = num
      
          def get_num(self):
              return self.__num
      
          def set_num(self, num):
              self.__num = num
      
      foo = Foo()
      print(foo.num)          # output: 0
      print(foo.get_num())    # output: 0
      print(foo._Foo__num)    # output: 0