user4998087
user4998087

Reputation: 347

Python can't set attribute on abstractproperty

I have the following snippet:

from abc import abstractproperty

class Base(object):
  @abstractproperty
  def foo(self):
    print 'wat'

class C(Base):
  def __init__(self):
    self.foo = 'test'
    self.bar = 'test2'

c = C()

When I execute it, it gives the stacktrace:

in __init__ AttributeError: can't set attribute

on the line self.foo = 'test'.

Anyone know why this is happening?

Upvotes: 1

Views: 2524

Answers (2)

Andrea Corbellini
Andrea Corbellini

Reputation: 17761

First of all, note that you forgot to use ABCMeta as your metaclass. Change your code to:

For Python 2.x:

from abc import ABCMeta

class Base(object):
    __metaclass__ = ABCMeta
    ...

For Python 3.x, any of the following:

from abc import ABC, ABCMeta

class Base(ABC):
    ...

class Base(metaclass=ABCMeta):
    ...

And you'll see this nice error:

Traceback (most recent call last):
  File "a.py", line 14, in <module>
    c = C()
TypeError: Can't instantiate abstract class C with abstract methods foo

It's telling you that you need a concrete implementation for your abstract property.

But that's not enough: to properly have self.foo = 'test' working, you need to implement a concrete setter for your property.

In the end, your code should look like this:

from abc import ABCMeta, abstractproperty

class Base(object):
    __metaclass__ = ABCMeta

    @abstractproperty
    def foo(self):
        print 'wat'

class C(Base):
    @property
    def foo(self):
        # do something

    @foo.setter
    def foo(self, value):
        # do something else

    def __init__(self):
        self.foo = 'test'
        self.bar = 'test2'

c = C()

Remember that you can use super() in your concrete property code to use the code from the abstract property.

Note: that since Python 3.3, @abstractproperty has been deprecated. Instead, you should use a combination of @property and @abstractmethod:

class Base(ABC):
    @property
    @abstractmethod
    def foo(self):
        ...

Upvotes: 4

jbndlr
jbndlr

Reputation: 5210

Your code defines a read-only abstractproperty. See docs on ABC. In addition, you did not set ABCMeta as meta class, which is obligatory. Furthermore, an abstractproperty is abstract which means that it has to be overwritten in the child class.

If you want to create a read-write abstractproperty, go with something like this:

from abc import ABCMeta, abstractproperty

class Base(object):
    __metaclass__ = ABCMeta # This is required.

    def getfoo(self):
        pass # Getter for preprocessing when property 'foo' is requested
    def setfoo(self, thefoo):
        pass # Setter for preprocessing when property 'foo' is set
    foo = abstractproperty(getfoo, setfoo)

class C(Base):
    foo = '' # Overwrite abstract property

    def __init__(self):
        self.foo = 'test'
        self.bar = 'test2'

Using the code above, you can instantiate your class C and set its property foo:

c = C()
print(c.foo)
>>> test

Upvotes: 1

Related Questions