代码之家  ›  专栏  ›  技术社区  ›  Srikanth

python属性和继承

  •  63
  • Srikanth  · 技术社区  · 16 年前

    我有一个基类,它有一个属性(get方法),我想在子类中覆盖它。我的第一个想法是:

    class Foo(object):
        def _get_age(self):
            return 11
    
        age = property(_get_age)
    
    
    class Bar(Foo):
        def _get_age(self):
            return 44
    

    这不起作用(子类bar.age返回11)。我找到了一个使用lambda表达式的解决方案:

    age = property(lambda self: self._get_age())
    

    那么,这是使用属性并在子类中覆盖它们的正确解决方案吗?还是有其他首选方法来实现这一点?

    10 回复  |  直到 16 年前
        1
  •  65
  •   Neil G    9 年前

    我只是想重复一下 property() 而且你会重复 @classmethod 重写类方法时的装饰器。

    虽然这看起来很冗长,但至少对于Python标准来说,你可能会注意到:

    1) 对于只读属性, property 可以用作装饰器:

    class Foo(object):
        @property
        def age(self):
            return 11
    
    class Bar(Foo):
        @property
        def age(self):
            return 44
    

    2) 在Python 2.6中, properties grew a pair of methods setter deleter 其可用于将只读属性已有的快捷方式应用于一般属性:

    class C(object):
        @property
        def x(self):
            return self._x
    
        @x.setter
        def x(self, value):
            self._x = value
    
        2
  •  11
  •   Kamil Kisiel    16 年前

    我不同意所选答案是允许覆盖属性方法的理想方式。如果你希望getter和setter被重写,那么你可以使用lambda来提供对self的访问,类似于 lambda self: self.<property func> .

    这至少适用于Python 2.4至3.6版本。

    如果有人知道如何使用property作为装饰器而不是直接的property()调用来实现这一点,我很想听听!

    例子:

    class Foo(object):
        def _get_meow(self):
            return self._meow + ' from a Foo'
        def _set_meow(self, value):
            self._meow = value
        meow = property(fget=lambda self: self._get_meow(),
                        fset=lambda self, value: self._set_meow(value))
    

    这样,可以很容易地执行覆盖:

    class Bar(Foo):
        def _get_meow(self):
            return super(Bar, self)._get_meow() + ', altered by a Bar'
    

    以便:

    >>> foo = Foo()
    >>> bar = Bar()
    >>> foo.meow, bar.meow = "meow", "meow"
    >>> foo.meow
    "meow from a Foo"
    >>> bar.meow
    "meow from a Foo, altered by a Bar"
    

    我发现这个 geek at play .

        3
  •  11
  •   Mr. B    7 年前

    另一种方法是,不必创建任何额外的类。我添加了一个set方法来显示如果你只覆盖其中一个,你会怎么做:

    class Foo(object):
        def _get_age(self):
            return 11
    
        def _set_age(self, age):
            self._age = age
    
        age = property(_get_age, _set_age)
    
    
    class Bar(Foo):
        def _get_age(self):
            return 44
    
        age = property(_get_age, Foo._set_age)
    

    这是一个相当做作的例子,但你应该明白。

        4
  •  7
  •   James Bennett    16 年前

    是的,这就是方法;属性声明在父类定义执行时执行,这意味着它只能“看到”父类上存在的方法的版本。因此,当你在子类上重新定义一个或多个方法时,你需要使用子类版本的方法重新声明属性。

        5
  •  2
  •   Federico A. Ramponi    16 年前

    一个可能的解决方法可能如下:

    class Foo:
        def __init__(self, age):
            self.age = age
    
        @property
        def age(self):
            print('Foo: getting age')
            return self._age
    
        @age.setter
        def age(self, value):
            print('Foo: setting age')
            self._age = value
    
    
    class Bar(Foo):
        def __init__(self, age):
            self.age = age
    
        @property
        def age(self):
            return super().age
    
        @age.setter
        def age(self, value):
            super(Bar, Bar).age.__set__(self, value)
    
    if __name__ == '__main__':
        f = Foo(11)
        print(f.age)
        b = Bar(44)
        print(b.age)
    

    它打印

    Foo: setting age
    Foo: getting age
    11
    Foo: setting age
    Foo: getting age
    44
    

    这个想法来自David Beazley的《Python Cookbook》;布莱恩·K·琼斯。 在Debian GNU/Linux 9.11上使用Python 3.5.3(扩展)

        6
  •  2
  •   Kozyarchuk    16 年前

    这样的方法会奏效

    class HackedProperty(object):
        def __init__(self, f):
            self.f = f
        def __get__(self, inst, owner):    
            return getattr(inst, self.f.__name__)()
    
    class Foo(object):
        def _get_age(self):
            return 11
        age = HackedProperty(_get_age)
    
    class Bar(Foo):
        def _get_age(self):
            return 44
    
    print Bar().age
    print Foo().age
    
        7
  •  2
  •   Community CDub    8 年前

    与…一致 @mr-b 只有装饰师。

    class Foo(object):
        def _get_meow(self):
            return self._meow + ' from a Foo'
        def _set_meow(self, value):
            self._meow = value
        @property
        def meow(self):
            return self._get_meow()
        @meow.setter
        def meow(self, value):
            self._set_meow(value)
    

    这样,可以很容易地执行覆盖:

    class Bar(Foo):
        def _get_meow(self):
            return super(Bar, self)._get_meow() + ', altered by a Bar'
    
        8
  •  0
  •   andrew pate    10 年前

    我在从子类设置父类中的属性时遇到了问题。以下工作组扩展了父级的属性,但是通过直接调用父级的_set_age方法来实现的。皱纹应该总是正确的。不过,这有点javathonic。

    import threading
    
    
    class Foo(object):
        def __init__(self):
            self._age = 0
    
        def _get_age(self):
            return self._age
    
        def _set_age(self, age):
            self._age = age
    
        age = property(_get_age, _set_age)
    
    
    class ThreadsafeFoo(Foo):
    
        def __init__(self):
            super(ThreadsafeFoo, self).__init__()
            self.__lock = threading.Lock()
            self.wrinkled = False
    
        def _get_age(self):
            with self.__lock:
                 return super(ThreadsafeFoo, self).age
    
        def _set_age(self, value):
            with self.__lock:
                self.wrinkled = True if value > 40 else False
                super(ThreadsafeFoo, self)._set_age(value)
    
        age = property(_get_age, _set_age)