Pycharm visual warning about unresolved attribute reference - python

I have two classes that look like this:
class BaseClass:
def the_dct(self):
return self.THE_DCT
class Kid(BaseClass):
THE_DCT = {'key': 'value'}
# Code i ll be running
inst = Kid()
print(inst.the_dct())
Inheritance has to be this way; second class containing THE_DCT and first class containing def the_dct.
It works just fine, but my problem is that i get a warning in Pycharm (unresolved attribute reference), about THE_DCT in BaseClass.
Is there a reason why it's warning me (as in why i should avoid it)?
Is there something i should do differently?

Within BaseClass you reference self.THE_DCT, yet when PyCharm looks at this class, it sees that THE_DCT doesn't exist.
Assuming you are treating this as an Abstract Class, PyCharm doesn't know that that is your intention. All it sees is a class accessing an attribute, which doesn't exist, and therefore it displays the warning.
Although your code will run perfectly fine (as long as you never instantiate BaseClass), you should really change it to:
class BaseClass(object):
THE_DCT = {}
def the_dct(self):
return self.THE_DCT

In addition to the existing answers, or as an alternative, you can use Type Hints. This satisfies PyCharm's warnings and also distinguishes the attribute as being inherited (or at least not native to the class). It's as simple as adding THE_DCT: dict at the very top of your class (before anything else).
class BaseClass(object):
THE_DCT: dict # Add a type-hint at the top of the class, before anything else
def the_dct(self):
return self.THE_DCT
class Kid(BaseClass):
THE_DCT = {'vars': 'values'}
I prefer this approach because it negates the need to unnecessarily add a placeholder attribute (self.THE_DCT = {}) and, because it's visually different than declaring an attribute, it can also negate the need for adding a comment next to the placeholder attribute to explain that it's inherited.

Related

Python Crash Course, Alien Invasion, Chapter 12, " Unresolved attribute reference 'draw_bullet' for class 'Sprite' " [duplicate]

I have two classes that look like this:
class BaseClass:
def the_dct(self):
return self.THE_DCT
class Kid(BaseClass):
THE_DCT = {'key': 'value'}
# Code i ll be running
inst = Kid()
print(inst.the_dct())
Inheritance has to be this way; second class containing THE_DCT and first class containing def the_dct.
It works just fine, but my problem is that i get a warning in Pycharm (unresolved attribute reference), about THE_DCT in BaseClass.
Is there a reason why it's warning me (as in why i should avoid it)?
Is there something i should do differently?
Within BaseClass you reference self.THE_DCT, yet when PyCharm looks at this class, it sees that THE_DCT doesn't exist.
Assuming you are treating this as an Abstract Class, PyCharm doesn't know that that is your intention. All it sees is a class accessing an attribute, which doesn't exist, and therefore it displays the warning.
Although your code will run perfectly fine (as long as you never instantiate BaseClass), you should really change it to:
class BaseClass(object):
THE_DCT = {}
def the_dct(self):
return self.THE_DCT
In addition to the existing answers, or as an alternative, you can use Type Hints. This satisfies PyCharm's warnings and also distinguishes the attribute as being inherited (or at least not native to the class). It's as simple as adding THE_DCT: dict at the very top of your class (before anything else).
class BaseClass(object):
THE_DCT: dict # Add a type-hint at the top of the class, before anything else
def the_dct(self):
return self.THE_DCT
class Kid(BaseClass):
THE_DCT = {'vars': 'values'}
I prefer this approach because it negates the need to unnecessarily add a placeholder attribute (self.THE_DCT = {}) and, because it's visually different than declaring an attribute, it can also negate the need for adding a comment next to the placeholder attribute to explain that it's inherited.

Python: How to type-hint a class variable?

Yes, I'm aware of the Enum class but I'm not interested in using it.
This is my Class enum:
class SomeClass:
SOME_STRING = "regular"
SOME_STRING2 = "important"
SOME_STRING3 = "accounts"
SOME_STRING4 = "special"
This is my type-hinted function:
def do_something(*, queue_name: SomeClass):
#....
And when I call it like this:
purge_queue(queue_name=SomeClass.SOME_STRING)
I get a warning:
Argument of type 'Literal['regular']' cannot be assigned to parameter 'queue_name' of type 'SomeClass'
'str' is incompatible with 'SomeClass'
What am I doing wrong?
A type hint of SomeClass means that values should be instances of SomeClass, not class variables of SomeClass. "One of SomeClass's class variables" isn't a type, and there is no annotation you can use with that meaning.
The stdlib solution for this kind of thing is enum.Enum, but you've rejected that. In that case, the next best thing is the typing.Literal type, introduced in Python 3.8. With that, you can write things like
SomeClassValue = typing.Literal['regular', 'important', 'accounts', 'special']
def do_something(*, queue_name: SomeClassValue):
...
You cannot somehow pull the type parameters from the class variables of SomeClass; it might run, but it won't type-check. This means you'll have to have a lot of error-prone code duplication if you go down this route. (Also, you'll probably need to put Final annotations on your class variables, to indicate they're not supposed to be reassigned.)

Default value for fields

I have problem accessing fields from an object in a python script I did. Basically it boils down to this little piece of code:
from enum import Enum
class AbstractFoo:
def __init__(self, foo='works', bar='nope'):
self.foo = foo
self.bar = bar
class Foo(Enum):
test = AbstractFoo('bla', 'tada')
So when in the python console I try to access an element of my enum with:
Foo.test.foo
I would expect it to print me 'bla' which is the value I pass to the constructor (or at least it should print 'works' which would be the default value I've assigned).
What I actually get is
AttributeError: 'Foo' object has no attribute 'foo'
you can probably tell by now that I'm pretty new to python and especially the concept objects in python (I mostly write code in Java which might lead to some missconceptions from my part about the behaviour of objects in python).
What I figured out though is that I can do:
Foo.test.foo = 'whatever'
and assign a value to foo in this way. But when doing this I can also assign a value to a field I haven't even specified in the constructor such as:
Foo.test.noField = 'shouldn't even exist'
and it will work just as fine which I don't understand at all.
I would be really glad about some clarification how objects work in python and/or how I could realize an enum of a class in python.
EDIT: Apparently the code behaves the way I want it to if I remove the inheritance from Enum.
That can be quite confusing, since you are literally saying that test is something and then it is not anymore. That is because Enum is a special kind of class that takes all of its members and "converts" them into instance of the class. So the type of test is not AbstractFoo anymore, but instead Foo. However, you can get back the original value assigned to the enum instance with the value property:
from enum import Enum
class AbstractFoo:
def __init__(self, foo='works', bar='nope'):
self.foo = foo
self.bar = bar
class Foo(Enum):
test = AbstractFoo('bla', 'tada')
print(Foo.test.value.foo)
>>> bla
As #jdehesa noted, Enum members are instances of their parent Enum class. If you need them to also be instances of some other class simply inherit from it as well:
class Foo(AbstractFoo, Enum):
test = 'bla', 'tada'
Note that you no longer need to call AbstractFoo directly.

Get PyCharm to know what classes are mixin for

Our application has set of complex form wizards. To avoid code duplication I created several mixins.
The problem is that PyCharm highlights mixin methods with Unresolved attribute refference error.
This is correct as object does not have such methods. But I know that this mixin will be used only with special classes. Is there any way to tell this info to PyCharm?
For now I use such approach:
class MyMixin(object):
def get_context_data(self, **kwargs):
assert isinstance(self, (ClassToBeExtended, MyMixin))
# super.get_context_data is still highlighter,
# as super is considered as object
context = super(MyMixin, self).get_context_data(**kwargs)
context.update(self.get_preview_context())
return context
def get_preview_context(self):
# without this line PyCharm highlights the self.initial_data
assert isinstance(self, (ClassToBeExtended, MyMixin))
return {'needs': (self.initial_data['needs']
if 'type' not in self.initial_data
else '%(needs)s %(type)s' % self.initial_data)}
While this works for some cases like autocomplete for self., it fails for other cases like super. Is there a better approach to achieve the desired behavior?
P.S.: I know that I can disable reference check for specific name or whole class, but I don't want to do this as it will not help in typo checks and autocomplete.
You can type-hint to PyCharm what kind of classes to expect.
class DictMixin(object):
def megamethod(
self, # type: dict
key
):
return self.get(key)
It's still not quite comparable to other type handling.
PyCharm is lazy in evaluating it, and only does so when first working on self.
Things are a bit tricky when accessing attributes of the mixin as well - self, # type: dict | DictMixin works for one of my classes, but not in my test code.
In python 3.5, you should be able to use # type: typing.Union[dict, DictMixin].
If you are creating Mixin, for, let's say ClassSub, which is subclass of ClassSuper, you can implement Mixins this way:
class Mixin1(ClassSuper):
pass
class Mixin2(ClassSuper):
pass
and then use them like:
class ClassSub(Mixin1, Mixin2):
pass
That way I use some mixins for models in Django. Also, django-extensions uses similar pattern (gives models that are actually mixins). Basically, this way you don't have to inherit ClassSuper, because it's "included" in every of your mixins.
Most important - PyCharm works like a charm this way.

how to override class, or undeclare class or redeclare a Class in python?

is there any possible to override class, or undeclare class or redeclare a Class in python?
Yes, just declare it again:
class Foo(object): x = 1
class Foo(object): x = 2
The above code will not raise any error, and the name Foo will refer to the second class declared. Note however, that the class declared by the first declaration will still exist if anything refers to it, e.g. an instance, or a derived class.
This means that existing instances will not change class when you declare a new class with the same name, and existing subclasses will not magically inherit from the new class.
Probably the simplest method to deal with subclasses is to also re-declare them, so they inherit from the "renewed" base class. An alternative would be to mess with their __bases__ property, although I can't tell you if that would have unexpected results (there will almost certainly be some corner cases where this would not work).
As to existing instances, it is possible to re-assign their __class__ property with a new class. This does present two issues - first you have to find them (see this question: Printing all instances of a class), and second of all, items stored in instance __dict__ or __slots__ properties will still be there in those instances. If that is not something that should happen with your new class definition, you will have to write appropriate code to handle that as part of the transformation.
IN summary, it's unlikely to be worth it except in quite simple cases. If you need complete uptime for a running system, you might be better using a replication-based approach to achieve code changes.
Update: If this is the kind of thing you know you're going to do, another solution would be to use the strategy pattern.
Undeclare a class using del className as usual.

Categories

Resources