简体   繁体   中英

Using property method with setter prevents function from binding to object attribute

I am attempting to use a property-based method to control the class constructor (screening for bad values at instance creation time), in this code. For some reason I don't understand the function set_sequence won't alter the derived class Binary object's attribute seq when called from the @num.setter method - it has to be called elsewhere. Am I missing something basic about how properties work?

class Number:
    def __init__(self, num):
        self.num = num

class Binary(Number):
    def __init__(self, num):
        super().__init__(num)
        self.seq = ()

    @property
    def num(self):
        return self._num

    @num.setter
    def num(self, value):
        if value < 0:
            raise ValueError('Unsigned numbers cannot be negative')
        self._num = value
        self.set_sequence() #this calls the function, but the value doesn't get bound

    def set_sequence(self):
        print('called ', end='')
        self.seq = tuple([int(x) for x in bin(self.num)[2:]])

Calling this code as follows:

if __name__ == "__main__":
    n1 = Binary(11)
    print(f"{n1.num} = {n1.seq}")
    n1.set_sequence()            
    print(f"{n1.num} = {n1.seq}")

Gives:

called 11 = ()
called 11 = (1, 0, 1, 1)

This throws an exception as expected when negative values are passed to constructor, but I don't understand why the function call fails to behave as expected. This pattern is based on Brett Slatkin's Item#29 in 'Effective Python' incidentally, the part about using @property to do type checking and value validation when the constructor is called.

因为在调用@num.setter super().__init__(num)之后的构造函数中,您使用self.seq = ()覆盖存储在 setter 方法中的值。

To have the desired output, you should do like this. In you example self.set_sequence() is overridden by the second instruction in the constructor.

class Number:
    def __init__(self, num):
        self.num = num

class Binary(Number):

    seq = ()

    def __init__(self, num):
        # or eventually here
        # seq = ()
        super().__init__(num)

    @property
    def num(self):
        return self._num

    @num.setter
    def num(self, value):
        if value < 0:
            raise ValueError('Unsigned numbers cannot be negative')
        self._num = value
        self.set_sequence() #this calls the function, but the value doesn't get bound

    def set_sequence(self):
        print('called ', end='')
        self.seq = tuple([int(x) for x in bin(self.num)[2:]])

if __name__ == "__main__":
    n1 = Binary(11)
    print(f"{n1.num} = {n1.seq}")
    n1.set_sequence()            
    print(f"{n1.num} = {n1.seq}")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM