NameError with super() inside decorator - python

Using python 2.7
I'm creating a library of objects on import using decorators, and while importing I do some checks on the instance of each object; mostly duplication checks...
I've recently made the switch to using super() to take advantage of its multiple-inheritance handling, but it raises a NameError on the object being instantiated.
Simplified code highlighting issue:
class Lib(object):
def __init__(self):
print "library created"
self.lib = {}
def add_obj(self, obj):
print "object being added to library --> %s" % obj.__name__
inst = obj()
print inst.name
self.lib[obj.__name__] = obj
def Reg(obj):
global test_lib
test_lib.add_obj(obj)
test_lib = Lib()
#Reg
class A(object):
def __init__(self):
object.__init__(self)
self.name = "A instance"
#Reg
class B(object):
def __init__(self):
super(B, self).__init__()
self.name = "B instance"
Output
>>> from testing import *
library created
object being added to library --> A
A instance
object being added to library --> B
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "testing.py", line 25, in <module>
class B(object):
File "testing.py", line 14, in Reg
test_lib.add_obj(obj)
File "testing.py", line 8, in add_obj
inst = obj()
File "testing.py", line 27, in __init__
super(B, self).__init__()
NameError: global name 'B' is not defined
It seems like there's a scoping issue? Excluding the decorator, class B instantiates without a problem.
Any suggestions?

You are attempting to instantiate B before it has finished being defined.
Fix it:
Remove these lines from Lib.add_obj():
inst = obj()
print inst.name
Why?
The decorator Reg is involved in the definition of B. So you will not be able to instantiate B, via that name, until Reg has exited.

Related

Why I got TypeError if calling staticmethod without class-body?

Why it happens? is this "binding behavior”?
using staticmethod in class body
>>> class Test:
#staticmethod
def test(msg="asd"):
print(msg)
>>> test = Test()
>>> test.test()
asd
but when I using it without, got error:
>>> #staticmethod
def test(msg=""):
print(msg)
>>> test
<staticmethod object at 0x10dde9be0>
>>> test()
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydevd_bundle/pydevd_exec2.py", line 3, in Exec
exec(exp, global_vars, local_vars)
File "<input>", line 1, in <module>
TypeError: 'staticmethod' object is not callable
A static method is a method that is bound to a class but does not require the class instance to function.
For example, you may have a class built around a file type to retrieve raw data from a source file and pass it to a lexer, your constructor could have you pass in the raw file however you may want another function to open the file, validate it and retrieve the data. In this case, you could use a static function within the class as the function is related to the class but doesn't require the class to function (in this case it would return the actual class).
A code example:
class PythonFile:
def __init__(self, raw_data):
self._raw_data = raw_data
self._lexer = None
#staticmethod
def open(fp):
with open(fp, "r") as rFile:
data = rFile.read()
# validate, etc...
return PythonFile(data)
def lex(self):
self._lexer = PythonLex(self._raw_data)
return self._lexer.get_master_block_array()
# etc, etc...
python_file = PythonFile.open("some\\path.py")
print(python_file.lex())
ItzTheDodo.
Use a standalone function.
def test(msg=""):
print(msg)

python linting and subclass properties

I have a superclass that uses some properties from the child class.
but unless I define properties in my superclass ALSO then the linter throws errors.
what's a pythonic way to get around this?
# parent
class DigItem:
def fetch_bq(self):
query = f'''select * from {self.table_id}'''
# subclass
class ChatLog(DigItem):
def __init__(self, set_name):
super().__init__(set_name)
self.table_id = biglib.make_table_id('chat_logs')
The above code errors with:
Instance of 'DigItem' has no 'table_id' memberpylint(no-member)
now, I can add the property to the superclass but that's pretty redundant and also risks overwriting the subclass
class DigItem:
def __init__(self, set_name):
self.table_id = None # set by child
This is down to the linter not being able to know AOT that this is a 'superclass' so it's fair enough as an error in a standalone instance.
But I'd prefer clean linting, pythonic code and not writing special hacky stuff just to shut up the linter.
In your example, DigItem has no __init__ at all (so it will be object's), so passing an argument to super().__init__() will fail
>>> class A: pass
...
>>> class B(A):
... def __init__(self):
... super().__init__("something")
...
>>> B()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
Further, you should (must) create the missing property in your parent in order for it to meaningfully make use of it in a method (otherwise different inheriting classes will not be able to make use of the method)
>>> class A:
... def foo(self):
... return self.bar
...
>>> class B(A):
... def __init__(self):
... self.bar = "baz"
...
>>> class C(A): pass # NOTE .bar is never defined!
...
>>> C().foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in foo
AttributeError: 'C' object has no attribute 'bar'
If the base class is not intended to be directly instantiable, consider making it an Abstract Base Class

Multiple Inheritance and variable access issue with Python v2.7.10

I'm wondering about this issue I'm getting with Python, I'm sure this is well known. I've been dabbling in Python now for a while and am getting used to it's flavor but I'm running into this issue outlined below. If you run this code below:
import pprint
pp = pprint.PrettyPrinter(indent=4).pprint
class Foo(object):
def __init__(self):
self.foo_var = 1
class Bar(Foo):
def __init__(self):
self.bar_var = 2
super(Foo, self).__init__()
def deep_access(self):
pp(self.bar_var)
class FooBar(Bar):
def __init__(self):
self.foobar_var = 3
super(Bar, self).__init__()
def access(self):
# call Bar
self.deep_access()
fb = FooBar()
fb.access()
You will receive the following error:
Traceback (most recent call last):
File "inheritance.py", line 29, in <module>
fb.access()
File "inheritance.py", line 26, in access
self.deep_access()
File "inheritance.py", line 16, in deep_access
pp(self.bar_var)
AttributeError: 'FooBar' object has no attribute 'bar_var'
From the error I gather it's looking for bar_var in FooBar rather than Bar, but if I call the parent class, why not use the variable declared in the parent?? How do I get a parent class to access it's own variables? It just seems weird to me coming from a different approach to OOP.
Instead of self.deep_access it tried Bar.deep_access(self) and super(FooBar, self).deep_access and super(Bar, self).deep_access and it doesn't work.
You are not calling super() correctly. The first argument should always be the class that's calling super, not any of its base classes..
change
class Bar(Foo):
def __init__(self):
self.bar_var = 2
super(Foo, self).__init__()
# ^^^
to
class Bar(Foo):
def __init__(self):
self.bar_var = 2
super(Bar, self).__init__()
# ^^^
and so on...
There's a nice article explaining most of the ins and outs of super() called "Super considered Super!"

Subclass of ConfigParser has no attribute _sections

I am trying to subclass ConfigParser. When trying to access _sections it says
'acc' object has no attribute '_sections'
Example Code(python 2.7):
import ConfigParser
class acc(object, ConfigParser.RawConfigParser):
def __init__(self, acc_file):
super(acc,self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)
The problem has been described much better here.
This is Python's Method Resolution Order.
So, what actually happens is that the MRO "chaining" does not behave well when encoutering an old style class. Or to be specific - classes that do not call super
In your code, configParser.RawConfigParser__init__(..) is never called. To fix this you could call it manually (simply add ConfigParser.RawConfigParser.__init__(self ...) in acc's init), although I'm not sure this is recommended or even valid.
Your other option is to make all classes conform to the new style, and invoke super, or old style, and initialize explicitly.
The only thing that seems to be working is if all classic-style classes are after all new-style classes in the output of Class.mro(), and specifically after object. This will prevent super from calling them.
The other answer isn't very safe because of this scenario:
class TheClassicClass:
def __init__(self):
print "instantiating clasic class!"
self._msg = "the classic class!"
def m(self):
print self._msg
class acc(ConfigParser.RawConfigParser, TheClassicClass, object):
def __init__(self, acc_file):
super(acc,self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)
a.m()
Fix to other answer: adding these lines to acc's __init__ should inform to explicitly instantiate the classes:
ConfigParser.RawConfigParser.__init__(self)
TheClassicClass.__init__(self)
To confirm the problem in your code, let's try to reproduce this problem in a simple setup...
We'll make old (classic) classes:
class AClassic:
def __init__(self):
print "init aclassic"
class BClassic:
def __init__(self):
print "init bclassic"
self.name = "bclassic"
def m(self):
print "print from " + self.name
And new style classes that invoke super:
class ANew(object):
def __init__(self):
print "init anew"
super(ANew, self).__init__()
class BNew(object):
def __init__(self):
print "init bnew"
super(BNew, self).__init__()
def m(self):
print "print from bnew"
In the simplest case, we're manually calling __init__ to make sure classes are instantiated. This would look something like:
class TestOldStyle(AClassic, BClassic):
def __init__(self):
AClassic.__init__(self)
BClassic.__init__(self)
self.m()
print "old style"
TestOldStyle()
In this case, depth-first, left-to-right, is the order of the MRO.
Output:
old style
init aclassic
init bclassic
print from bclassic
Now let's try new style, which looks like this with super:
# will init ANew and BNew
class TestNewSuper(ANew, BNew, object):
def __init__(self):
super(TestNewSuper, self).__init__()
TestNewSuper()
Since both classes call super, they are both instantiated and the output is:
init anew
init bnew
Now, we try using some "hybrids" of a classic-style classes (ConfigParser.RawConfigParser is a classic-style-class, and cannot call super:
# will init ANew , but not BClassic
class TestOldStyleSuper(ANew, BClassic):
def __init__(self):
super(TestOldStyleSuper, self).__init__()
self.m()
Output:
init anew
And immediately an exception:
Traceback (most recent call last):
File "./inhert.py", line 35, in <module>
TestOldStyleSuper()
File "./inhert.py", line 33, in __init__
self.m()
File "./inhert.py", line 15, in m
print "print from " + self.name
AttributeError: 'TestOldStyleSuper' object has no attribute 'name'
This happens since BClassic is not instantiated.
More examples of unexpected behaviour:
# init ANew, BClassic
class TestHybrid(ANew, BClassic, BNew):
def __init__(self):
super(TestHybrid, self).__init__()
self.m()
TestHybrid()
will initialize ANew and BClassic, but not BNew:
init anew
init bclassic
print from bclassic
And creating an incosistent MRO:
# no consistent MRO exception
class TestHybrid(ANew, object, BNew):
def __init__(self):
super(TestHybrid, self).__init__()
self.m()
TestHybrid()
Exception:
Traceback (most recent call last):
File "./inhert.py", line 33, in <module>
class TestHybrid(ANew, object, BNew):
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases BNew, object
Reverse the order of subclassing as follows:
import ConfigParser
class acc(ConfigParser.RawConfigParser, object):
def __init__(self, acc_file):
super(acc, self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)

Attribute Error Python

I'm having a python problem with a simple program. The program is supposed to allow the user to make a make a Cow() instance and give the cow a name in the parameter.
class Cow():
def __init__(self, name):
self.name = name
if self.name == None:
raise NoNameCowError("Your cow must have a name")
def speak(self):
print self.name, "says moo"
Now when I do
cow.Cow("Toby")
I get the error
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
cow.Cow("Toby")
File "C:\Users\Samga_000\Documents\MyPrograms\cow.py", line 8, in __init__
self.name = name
AttributeError: Cow instance has no attribute 'name'
Help? I originally thought I did something wrong with the exception but it doesn't seem to be that. Thanks in advance.
I think you modified your source code and didn't reloaded the module:
Buggy version:
class Cow():
def __init__(self, name):
if self.name == None:
raise NoNameCowError("Your cow must have a name")
def speak(self):
print self.name, "says moo"
>>> import so
Error raised as expected:
>>> so.Cow('abc1')
Traceback (most recent call last):
File "<ipython-input-4-80383f90b571>", line 1, in <module>
so.Cow('abc1')
File "so.py", line 3, in __init__
if self.name == None:
AttributeError: Cow instance has no attribute 'name'
Now let's modify the source code and add this line self.name = name:
>>> import so
>>> so.Cow('abc1')
Traceback (most recent call last):
File "<ipython-input-6-80383f90b571>", line 1, in <module>
so.Cow('abc1')
File "so.py", line 3, in __init__
self.name = name
AttributeError: Cow instance has no attribute 'name'
eh! still same error? That's because python is still using the old .pyc file or the cached module object. Just reload the module and updated code works fine:
>>> reload(so)
<module 'so' from 'so.py'>
>>> so.Cow('dsfds')
<so.Cow instance at 0x8b78e8c>
From docs:
Note For efficiency reasons, each module is only imported once per
interpreter session. Therefore, if you change your modules, you must
restart the interpreter – or, if it’s just one module you want to test
interactively, use reload(), e.g. reload(modulename).
A better version of your code:
class Cow():
def __init__(self, name=None): #Use a default value
self.name = name
if self.name is None: #use `is` for testing against `None`
raise NoNameCowError("Your cow must have a name")
I'm staring at the name check; this object requires the name argument does it not?
if self.name == None:
raise NoNameCowError("Your cow must have a name")
I'm a little Python thick, but that looks like a required arg.
When your declaring Cow, you need to pass object in the parentheses:
class Cow(object):
rest of code.
This way Python knows the class your declaring is an object with attributes and methods.

Categories