In addition to needing to inherit from object, properties only work on instances.
a = A()
a.db.doSomething("blah")
To make a property work on the class, you can define a metaclass. (A class is an instance of a metaclass, so properties defined on the metaclass work on the class, just as properties defined on a class work on an instance of that class.)
Answer from kindall on Stack OverflowIn addition to needing to inherit from object, properties only work on instances.
a = A()
a.db.doSomething("blah")
To make a property work on the class, you can define a metaclass. (A class is an instance of a metaclass, so properties defined on the metaclass work on the class, just as properties defined on a class work on an instance of that class.)
You aren't using classes correctly. A class is (normally) two things:
- A factory for creating a family of related objects
- A definition of the common behaviour of those objects
These related objects are the instances of the class. Normal methods are invoked on instances of the class, not on the class itself. If you want methods that can be invoked from the class, without an instance, you need to label the methods with @classmethod (or @staticmethod).
However I don't actually know whether properties work when retrieved from a class object. I can't check right now, but I don't think so. The error you are getting is that A.db is retrieving the property object which defines the property itself, it isn't "evaluating" the property to get A.__db. Property objects have no doSomething attribute. Properties are designed to be created in classes as descriptions of how the instances of those classes work.
If you did intend to be working with an instance of A, then you'll need to create one:
my_a = A()
my_a.db.doSomething("blah")
However, this will also fail. You have not correctly written getDB as any kind of method. Normal methods need an argument to represent the instance it was invoked on (traditionally called self):
def getDB(self):
...
Static methods don't, but need a decorator to label them as static:
@staticmethod
def getDB():
...
Class methods need both an argument to receive the class they were invoked on, and a decorator:
@classmethod
def getDB(cls):
...
Your indentation is goofed, and you've mixed tabs and spaces. Run the script with python -tt to verify.
If you’re using python 3+ this may also occur if you’re using private variables that start with double underscore, e.g., self.__yourvariable. Just something to take note of for some of you who may run into this issue.
oop - Python Attribute Error: type object has no attribute - Stack Overflow
python - Why do I receive the following AttributeError when testing a class method with unittest: 'class' object has no attribute 'testing method'? - Stack Overflow
python - "AttributeError: 'property' object has no attribute 'get'" when using Depends in FastAPI - Stack Overflow
python - AttributeError: 'cached_property' object has no attribute 'setter' - Stack Overflow
Videos
You are not creating an instance, but instead referencing the class Goblin itself as indicated by the error:
AttributeError: type object 'Goblin' has no attribute 'color'
Change your line to Azog = Goblin()
When you assign Azog = Goblin, you aren't instantiating a Goblin. Try Azog = Goblin() instead.
@cached_property is designed for immutable values. You cannot expect that this decorator will mimic all features from standard property. In python doc you can find: "The mechanics of cached_property() are somewhat different from property(). A regular property blocks attribute writes unless a setter is defined. In contrast, a cached_property allows writes."
cached_property does not require a setter:
>>> class A:
... @functools.cached_property
... def a(self):
... return 1
...
>>> z = A()
>>> z.a
1
>>> z.a = 2
>>> z.a
2
>>> y = A()
>>> y.a = 2
>>> y.a
2
No, autospeccing cannot mock out attributes set in the __init__ method of the original class (or in any other method). It can only mock out static attributes, everything that can be found on the class.
Otherwise, the mock would have to create an instance of the class you tried to replace with a mock in the first place, which is not a good idea (think classes that create a lot of real resources when instantiated).
The recursive nature of an auto-specced mock is then limited to those static attributes; if foo is a class attribute, accessing Foo().foo will return an auto-specced mock for that attribute. If you have a class Spam whose eggs attribute is an object of type Ham, then the mock of Spam.eggs will be an auto-specced mock of the Ham class.
The documentation you read explicitly covers this:
A more serious problem is that it is common for instance attributes to be created in the
__init__method and not to exist on the class at all.autospeccan’t know about any dynamically created attributes and restricts the api to visible attributes.
You should just set the missing attributes yourself:
@patch('foo.Foo', autospec=Foo)
def test_patched(self, mock_Foo):
mock_Foo.return_value.foo = 'foo'
Bar().bar()
or create a subclass of your Foo class for testing purposes that adds the attribute as a class attribute:
class TestFoo(foo.Foo):
foo = 'foo' # class attribute
@patch('foo.Foo', autospec=TestFoo)
def test_patched(self, mock_Foo):
Bar().bar()
There is only a create kwarg in patch that, when set to True, will create the attribute if it doesn't exist already.
If you pass in create=True, and the attribute doesn’t exist, patch will create the attribute for you when the patched function is called, and delete it again after the patched function has exited.
https://docs.python.org/3/library/unittest.mock.html#patch