How to prevent Python class instance variable typo? - python

A python class function had something like this:
class Widget:
def __init__(self):
self.foo_bar = 0
def fun(self, xyz):
self.foobar = xyz
A typo. The code for fun() should have referenced self.foo_bar. This took surprisingly long to debug (as the actual functions were more complex).
Is there a way to enforce that class instance variables can only be introduced (declared) in __init__? Python happily created a new class instance variable instead of generating an error. An error would have saved a lot of time.

If I run pylint on this code, I get the following errors:
test.py:1:0: C0114: Missing module docstring (missing-module-docstring)
test.py:1:0: C0115: Missing class docstring (missing-class-docstring)
test.py:5:4: C0116: Missing function or method docstring (missing-function-docstring)
test.py:6:8: W0201: Attribute 'foobar' defined outside __init__ (attribute-defined-outside-init)
test.py:1:0: R0903: Too few public methods (1/2) (too-few-public-methods)
You can configure pylint to disable the warnings you might not care as much about, e.g. missing-module-docstring, but the one that catches your attribute typo is this one:
test.py:6:8: W0201: Attribute 'foobar' defined outside __init__ (attribute-defined-outside-init)

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.

What is the best practice for metaclass methods that call each other?

I'm trying to build a metaclass, and I was just thinking I was really getting it. And I want to have class methods for each of the instances of this metaclass.
class MyMeta(type):
"""A metaclass"""
def __new__(mcs, name, bases, attributes):
pass
def _foo(cls):
pass
def _bar(cls):
cls._foo()
When I run pylint on it, the cls._foo has difficulty:
[pylint] E1120:No value for argument 'cls' in unbound method call
When I try running the code (My code is more complex than this) it appears to be running fine, and doing what I'm expecting it to do. So how am I supposed to fix this case? What does it mean precisely.
It sounds like other errors related to not properly declaring things as #staticmethods, but I can't mark this as a #classmethod because then it would be a Metaclass method.
Related searches seem to be talking about places where people are dynamically adding constructors or something, and I don't think this is that case. Am I misunderstanding something still about metaclasses?
What I really want is class methods that call each other defined in a metaclass. Is there a better way to do this?
[pylint] E1120:No value for argument 'cls' in unbound method call
As far as your metaclasss is concerned, your _foo and _bar methods are just ordinary Python methods - which means Python will fill in a reference to each class (metaclass instance) automatically when calling these methods.
And, as this is a metaclass, for clarity, semantics, and readability, it is much better to do as you did: name their first parameter as cls instead of self.
The problem is just that pylint does not knw about that: it is likely it is expecting a hard-coded self.
Your code, in this respect is irretouchable.
All you have to do is to add a meta-comment fo that pylint ignore these lines - fortunatelly, Pylint allows it at block level, unlike some tools where you have to mark that in each line.
class MyMeta(type):
"""A metaclass"""
# pylint: disable=no-value-for-parameter
def __new__(mcs, name, bases, attributes):
pass
def _foo(cls):
pass
def _bar(cls):
cls._foo()
Your example metaclass, with the "meta-comment" so that pylint ignores your non-error.

Inner class usage python. Global name is not defined

I have a question about inner classes usage in python. I know that this is a bad practice but anyway. I am wondering about scope where inner class is defined. I got an error 'global name 'ClassName' is not defined'. My code snippet looks like this:
I discovered that to avoid getting this error I can use:
ClassWithEnum.EnumClass
instead of:
EnumClass
inside of doSomethingWithEnum() function. So I am wondering if there is any other way to make EnumClass defined inside of doSomethingWithEnum() function? And why there is no such error when I declare EnumClass as a default parameter in doSomethingWithEnum() function?
class ClassWithEnum(object):
class EnumClass(object):
...
def doSomethingWithEnum(self, m = EnumClass....):
...
Python class construction executes as code. The def statement is really a line of code being executed that creates a function. The class keyword introduces a namespace. Putting these two mechanisms together, it means that class EnumClass really creates an object by that name in the current namespace, not much different from what foo = 'bar' does, so within the same namespace you can refer to it by that name, which is what happens in the def statement.
Also compare:
class Foo:
bar = 'baz'
print bar
baz = bar
Every line of code inside a class block is a regular executable line of code.
Once your class definition is done, you're out of the ClassWithEnum namespace and cannot access EnumClass anymore simply by that name; it's now only available as ClassWithEnum.EnumClass; whether from "outside" the class or from within a function (any function, including class methods).
To get access to the class without typing its name from within the method you could do:
type(self).EnumClass
Or simply self.EnumClass, since properties are looked up up the chain.
When you are inside doSomethingWithEnum function, you are in the different namespace. To access anything that is defined in your class, like EnumClass, you should call self.EnumClass. If it were a class method, it would be cls.EnumClass.

Pycharm visual warning about unresolved attribute reference

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 class not in globals when using decorator

Maybe the title is a bit misleading, however I wanted to create a simple decorator to decorate some class methods as "allowed" in an RPC mechanism, but I'm stuck on a strange error when trying to access class variables (Python 2.7.5). Check the code below:
class myclass():
rpcallowedmethods = []
def __init__(self):
pass
def rpcenabled(fn):
print fn
print globals()
print myclass
#rpcenabled
def somefunc(self,param):
pass
c = myclass()
Exception: NameError: global name 'myclass' is not defined
Anyone can explain the reason behind this to me?
EDIT:
What I'm asking is more about the fact that python executes the decorator defined in a class and run against decorated classmethods even prior having the class in the globals, so I believed it's more of a logical "bug" in the python implementation than a seemingly obvious NameError
The actual class object is only assigned to its name after its definition is finished. Thus you cannot use the class name during its definition. You can either create a decorator outside of the class to which you explicitly pass the list you want to fill, or use the following:
class myclass():
rpcmethods = []
def _rpcallowed(fct, l=rpcmethods):
l.append(fct)
return fct
#_rpcallowed
def myfct(): pass
Note that the default parameter (l=rpcmethods) is a workaround as you cannot access a class variable inside of a function without a reference to the class or an instance.
The variant with the decorator outside of the class would probably qualify as being "cleaner" than this as it's explicit and reusable, but it would be a bit more code and less specific.
You're abusing decorators. A decorator is meant to add something to thing object is given. "decorating" it somehow.
The more usual way to do something like this would be to decorate both the method and the class. Metaclasses are another way to solve this problem. They're more powerful, but are overkill for your current problem. However, directly decorating the functions might be all you need to do. And save collating the rpc functions for when a proxy is made.
from types import FunctionType
def enable_rpc(func):
func.rpc_enabled = True
return func
def rpc_enabled_class(cls):
functions = [attr for attr in vars(cls).values()
if isinstance(attr, FunctionType)]
cls._rpc_enabled_methods = [
func for func in functions
if getattr(func, "rpc_enabled", False)
]
return cls
#rpc_enabled_class
class SampleClass(object):
#enable_rpc
def my_func(self):
pass
print(SampleClass._rpc_enabled_methods)
Strange error?
print myclass
caused the error. You can't use the name myclass in its definition...

Categories

Resources