Custom class with recursion in methods, global name not defined - python

I'm trying to implement a method for which is necessary to use recursion, but every time, I get the global name not defined error
My class look like this:
class MyClass(object):
def _init_(self, name=None, content=None):
self.name = name
self.content = content
It's a node class, name it's just a text string and content a list of it's children (they are nodes too), is initialized as None but the construction function that builds the tree give them a blank list if they have no children. The class works fine and so does the function but if I try to add recurtion to methods they just don't work, even if they work just fine as a standalone function, i.e.:
def get_nodes(self):
c = []
c.append(self.name)
if self.content != []:
for a in self.content:
c.extend(get_nodes(a))
return c
I know this is possible, what am I doing wrong?

You need to do a.get_nodes().
Also the initialization method is called __init__, not _init_ (two underscores on both ends).
Edit: If you won't show your code, we can't tell you what's wrong with your code. This code works for me:
class MyClass(object):
def __init__(self, name=None, content=None):
self.name = name
self.content = content
def get_nodes(self):
c = []
c.append(self.name)
if self.content != []:
for a in self.content:
c.extend(a.get_nodes())
return c
>>> n = MyClass('me', [])
>>> m = MyClass('other', [n])
>>> m.get_nodes()
['other', 'me']
If your code doesn't work then you have to explain how your code is different from that.

Related

Python: Inner Class

I am trying to create a json string from a class and I defined my class as follows:
import json
import ast
from datetime import datetime
import pytz
import time
class OuterClass:
def __init__(self):
self.Header = None
self.Body = None
class Header:
def __init__(self, ID = None, Name = None):
self.ID = ID
self.Name = Name
class Body:
def __init__(self, DateTime=None, Display=None):
self.DateTime = DateTime
self.Display = Display
def current_time_by_timezone(timezone_input):
return datetime.now(pytz.timezone(timezone_input))
if __name__ == '__main__':
response = OuterClass()
header = response.Header('123', 'Some Name')
body = response.Body(current_time_by_timezone('US/Central'), 'NOT VALID')
print(json.dumps(response.__dict__))
I'm getting an error 'TypeError: 'NoneType' object is not callable'. Is it because I'm setting the Header and Body in the OuterClass definition myself to None?
The problem with your code is these lines:
self.Header = None
self.Body = None
These create instance variables named Header and Body on every instance of OuterClass, so you can never access the class variables (the nested classes) via an instance, only via OuterClass itself.
It's not very clear what your intention is with this data structure. Defining a class inside another class doesn't do anything special in Python (by default, you could probably make there be special behavior with special effort, like using a metaclass that makes the inner classes into descriptors). Generally though, there's no implied relationship between the classes.
If you want your OuterClass to create instances of the other two classes, you can do that without nesting their definitions. Just put the class definitions at top level and write a method that creates an instance at an appropriate time and does something useful with it (like binding it to an instance variable).
You might want something like:
def Header:
...
def Response:
def __init__(self):
self.header = None
def make_header(self, *args):
self.header = Header(*args)
return self.header
You could keep the classes nested as long as you don't expect that to mean anything special, just be sure that you don't use the class name as an instance variable, or you'll shadow the name of the nested class (a capitalization difference, like self.header vs self.Header could be enough).

Python object inside other objects are not isolated

I was expecting the object items inside other python objects are isolated.
However, the following code shows my expected result is wrong. It seems python uses a central item_list for all Group items. How can I fix this?
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = []
def __init__(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Result
len = 4
bb2 = bb1
Version
python3 --version
Python 3.5.2
Well, first of all we should make a difference between class attributes and instance attributes. A class attribute (like item_list) belongs to the class itself (in this case "Group"), so it will be accessible by calling Group.item_list. On the other hand, you can define a item_list for every instance of Group by defining self.item_list = [] inside the Group class constructor (__init__).
The Group.item_list array will be unique for the whole class, and thus will be suitable to store things that you want to share across instances (such as g2).
The self.item_list array (that will be different for each instance of Group) will hold values exclusively for the instance itself, so each Group will have its own item_list.
I think you are aiming for the second approach (instance variables) so you should move the definition of item_list = [] inside the class constructor.
The result should look like this:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
item1 = Item("itemName")
group1 = Group(item1)
# This should print an array containing the *item1* instance
print(group1.item_list)
print(group1.item_list[0] == item1)
Variables that are declared outside of the __init__ method (in this case item_list) are shared between all instances of a class (called class variables), which is why your expected result is wrong.
On the other hand, variables inside the __init__ only belong to the given instance of that class.
Your using class variables, which are similar C++ static variables inside classes (i.e. that variable is shared by ALL class instances). You need to put it inside the __init__ (constructor) to make it so each class creates its own version:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
# Though typically you would also have a function like this:
def add_item(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Instance vs class attributes is covered in other answers. I want to add that you can avoid having shared instance variables by using an immutable type (e.g. tuple) instead of a mutable type (e.g. list) for class attributes. Like that they won't be shared among instances while still allowing you to define class attributes.
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = tuple()
def __init__(self, item):
self.item_list += (item,)

Create an instance of a class inside the same class and use the same class as parameter

I have the following class:
class state(list):
def __init__(self,parent = None, path = None, *args):
list.__init__(self, *args)
self.n = len(self)
self.parent = parent
self.path = path
def neighbors(self):
lista = []
if self.__hasUp():
lista.append(state(Self,'Up',self.__swap(self.index(0),self.index(0) - 3))))
if self.__hasDown():
lista.append(state(Self,'Down',self.__swap(self.index(0),self.index(0) + 3))))
if self.__hasLeft():
lista.append(state(Self,'Left',self.__swap(self.index(0),self.index(0) - 1))))
if self.__hasRight():
lista.append(state(Self,'Right',self.__swap(self.index(0),self.index(0) + 1))))
return lista
I normally create an instance of the class as follow:
inicial = state(None,None,[1,2,5,3,4,0,6,7,8])
I need to do the followin (I think if I put the whole context of what I am doing, it will be missleading):
anotherList = []
for neighbor in inicial.neighbors():
anotherList.append(neighbor)
I just want to have a list with few extra attributes and methods. The problem is I need this class to create istances of itself with the object that is creating them as a parameter in the neighbors method. I have testet all methods referenced in the code and they work as expected, but I just think they are not need for this question and they will make it a really long question.
I have also checked this Declaring a class with an instance of it inside in Python, Create static instances of a class inside said class in Python and this Class constructor able to init with an instance of the same class object. However, I do not understand it yet :(
I am quite an amateur,so if you have any suggestions, they are all welcome ;)
I am sorry..I tried and it turns out it is very straightforward. It was simply a mistake from the beggining. I leave the answer here in case someone needs it, but if you suggest I delete it (is it possible?) I'will do it.
class test(list):
def __init__(self,pa, name, *args):
list.__init__(self, *args)
self.name = name
self.par = pa
def create(self):
lista = []
lista.append(test(self,'a',[0,1]))
lista.append(test(self,'b',[0,2]))
return lista
Best regards,

Create multiple classes or multiple objects in Python?

I have the following problem and I need advice on how to solve it the best technically in Python. As I am new to programming I would like to have some advice.
So I will have the following object and they should store something. Here is an example:
object 1: cash dividends (they will have the following properties)
exdate (will store a list of dates)
recorddate (will store a list of dates)
paydate (will store a list of dates)
ISIN (will store a list of text)
object 2: stocksplits (they will have the following prpoerties)
stockplitratio (will be some ration)
exdate(list of dates)
...
I have tried to solve it like this:
class cashDividends(object):
def __init__(self, _gross,_net,_ISIN, _paydate, _exdate, _recorddate, _frequency, _type, _announceddate, _currency):
self.gross = _gross
self.net = _net
self.ISIN = _ISIN
self.paydate = _paydate
self.exdate = _exdate
self.recorddate = _recorddate
self.frequency = _frequency
self.type = _type
self.announceddate = _announceddate
self.currency = _currency
So if I have this I would have to create another class named stockplits and then define an __init__ function again.
However is there a way where I can have one class like "Corporate Actions" and then have stock splits and cashdividends in there ?
Sure you can! In python you can pass classes to other classes.
Here a simple example:
class A():
def __init__(self):
self.x = 0
class B():
def __init__(self):
self.x = 1
class Container():
def __init__(self, objects):
self.x = [obj.x for obj in objects]
a = A()
b = B()
c = Container([a,b])
c.x
[0,1]
If I understood correctly what you want is an object that has other objects from a class you created as property?
class CorporateActions(object):
def __init__(self, aCashDividend, aStockSplit):
self.cashDividend = aCashDividend
self.stockSplit = aStockSplit
myCashDividends = CashDividends(...) #corresponding parameters here
myStockSplit = StockSplit(...)
myCorporateActions = CorporateActions(myCashDividends, myStockSplit)
Strictly speaking this answer isn't an answer for the final question. However, it is a way to make your life slightly easier.
Consider creating a sort-of template class (I'm using this term loosely; there's no such thing in Python) that does the __init__ work for you. Like this:
class KwargAttrs():
def __init__(self, **kwargs):
for k,v in kwargs.items():
setattr(self, k, v)
def _update(self, **kwargs):
args_dict = {k:(kwargs[k] if k in kwargs else self.__dict__[k]) for k in self.__dict__}
self.__dict__.update(args_dict)
This class uses every supplied keyword argument as an object attribute. Use it this way:
class CashDividends(KwargAttrs):
def __init__(self, gross, net, ISIN, paydate, exdate, recorddate, frequency, type, announceddate, currency):
# save the namespace before it gets polluted
super().__init__(**locals())
# work that might pollute local namespace goes here
# OPTIONAL: update the argument values in case they were modified:
super()._update(**locals())
Using a method like this, you don't have to go through the argument list and assign every single object attribute; it happens automatically.
We bookend everything you need to accomplish in the __init__ method with method calls to the parent-class via super(). We do this because locals() returns a dict every variable in the function's current namespace, so you need to 1.) capture that namespace before any other work pollutes it and 2.) update the namespace in case any work changes the argument values.
The call to update is optional, but the values of the supplied arguments will not be updated if something is done to them after the call to super().__init__() (that is, unless you change the values using setattr(self, 'argname, value)`, which is not a bad idea).
You can continue using this class like so:
class StockSplits(KwargAttrs):
def __init__(self, stocksplitratio, gross, net, ISIN, paydate, exdate, recorddate, frequency, type, announceddate, currency):
super().__init__(**locals())
As mentioned in the other answers you can create a container for our other classes, but you can even do that using this same template class:
class CorporateActions(KwargAttrs):
def __init__(self, stock_splits , cash_dividends):
super().__init__(**locals())
ca = CorporateActions(stock_splits = StockSplits(<arguments>), cash_dividends = CashDividends(<arguments>) )

Python global name not defined

I'm trying to test the following simple object:
class WebCorpus(object):
def __init__(self):
_index = {}
_graph = {}
_ranks = {}
_corpusChanged = False
def lookup(self, keyword):
if keyword in _index:
return _index[keyword]
return None
# (some irrelevant code)
With:
from WebCorpus import WebCorpus
def test_engine():
print "Testing..."
content = """This is a sample webpage with
two links that lead nowhere special."""
outlinks = ["http://www.example.com", "http://www.go.to"]
corpus = WebCorpus()
assert corpus.lookup("anything") == None
#(some more code)
test_engine()
But it gives me an error: NameError: global name '_index' is not defined. I don't understand this, _index is clearly defined in the __init__ !? What is my mistake here?
Help appreciated.
In order to set class variables in the class method, you should use self:
class WebCorpus(object):
def __init__(self):
self._index = {}
self._graph = {}
self._ranks = {}
self._corpusChanged = False
def lookup(self, keyword):
if keyword in self._index:
return self._index[keyword]
return None
Or, you can simplify the code and set variables like this (I've also simplified lookup method):
class WebCorpus(object):
_index = {}
_graph = {}
_ranks = {}
_corpusChanged = False
def lookup(self, keyword):
return self._index.get(keyword)
Note, the second example is not equivalent to the first one, because class-level variables are used, see comments below.
What's happening here is that it's defining _index but then losing it after the __init__ is run. You should append self to everything, so it's self._index, etc. This goes for the entire class, not just in the __init__.

Categories