I created many instances of a PlotHandler class. An instance must keep it's variables private. But the way I managed them led to a hard to detect problem, a private list variable is shared between instances! And that too without any obvious source for the leak.
My debugging told me that the private member function that modifies the list sees the same list, even if they are different objects.
Is this a "gotcha" problem? What is the best way to troubleshoot this?
Here are the relevant parts (I hope they are!) of the implementation. Please see the ALL-CAPS comments:
The file implementing PlotHandler:
class PlotHandler(wx.Frame):
__crop_section = None
__projection = None
__crop_xcord = None
_band_data = [] #THIS GETS SHARED
def _on_plot_click(self, xcord): #CALLED BY ANOTHER OBJECT
band = self._analyze_band( xcord )
self._band_data.append(band)
...
The parent class that it is managing PlotHandlers:
class MainFrame(wx.Frame):
__close_callback__ = None
_plot_handlers = []
def __init__(self, parent, title):
...
def InitUI(self):
...
img_handler = ImageHandler(panel)
self.img_src.register_callback( img_handler.update_image )
#you need to call PlotHandler(parent, cropped)
img_handler.register_sample_callback( self._create_new_plot_handler )
...
def _create_new_plot_handler(self, cropped_sample ):
self._plot_handlers.append( PlotHandler(self, cropped_sample) ) #CREATE THEM
See this question, this one, and tons of other stuff you can find by googling "Python class variables shared", "Python FAQ class variables", etc.
The short answer is: variables defined directly in the class body are class variables, not instance variables, and are thus shared among instances of the class. If you want instance variables you must assign them from within a method, where you have access to self.
Class attributes are shared between instances. If you want to define an instance attribute (so each object have its own reference to the variable) you have to define it in __init__
class PlotHandler(wx.Frame):
__crop_section = None
__projection = None
__crop_xcord = None
def __init__(self, **kwargs):
self._band_data = [] #THIS IS NOT SHARED
Related
I'm trying to make a variable as an attribute in a class but at the same time this variable have to be calculated in a method inside this class . Here is my code:
class entrepot :
def __init__(self,L_R,L_A,pos_porte,longueur,largeur):
self.L_R=L_R
self.L_A=L_A
self.pos_porte=pos_porte
self.longueur=longueur
self.largeur=largeur
def matrice_expedition(self):
A=np.zeros((longueur,largeur-2))
return (A)
am I allowed to add A as attribute? it sounds a stupid question but i'm still a beginner
Do you mean something like this ?
# py2 / py3 compat: inherit from object for py2
# pep08 : class names should be CamelCase
class Entrepot(object):
def __init__(self,L_R,L_A,pos_porte,longueur,largeur):
self.L_R=L_R
self.L_A=L_A
self.pos_porte=pos_porte
self.longueur=longueur
self.largeur=largeur
self.A = self.matrice_expedition()
def matrice_expedition(self):
# Python has no implict `this`, you need to
# use `self.XXX` to access the current instance
# attributes
A = np.zeros((self.longueur,self.largeur-2))
return A
Note that while technically legal, this code will not ensure that self.A stays consistant with self.longeur and self.largeur so if either of those attributes change later, you may have some issues.
If the warehouse (entrepot) size is not supposed to change after initialization, you can make longueur and largeur "protected" attributes by renaming them to _longueur and _largeur (note that this is only a naming convention - it won't prevent access to those attributes - but it's a very strong convention that tells users of your class that they should not mess with those attributes and are on their own if they do and break anything).
Also if you still want to provide public read access to longueur and largeur you can make them read-only properties:
class Entrepot(object):
def __init__(self, L_R, L_A, pos_porte, longueur, largeur):
self.L_R = L_R
self.L_A = L_A
self.pos_porte = pos_porte
self._longueur=longueur
self._largeur=largeur
self.A = self.matrice_expedition()
#property
def longueur(self):
return self._longueur
#property
def largeur(self):
return self._largeur
def matrice_expedition(self):
# ....
Actually, you should only expose as "public" attributes (and methods) the attributes and methods that are supposed to be used by the client code (chances are you want to make "self.A" a protected attribute too)
I am trying to make a class within a module, import that module file in my controller, and then reference the class that is defined within that module, but I keep getting a message that reads NameError("name 'self' is not defined")
Here is my code in my created module:
from gluon import *
class device_info(object):
self.info = {}
def __init__(self, info):
self.info = info
return
def setInfo(info):
self.info = info
return
def getInfo():
return self.info`
Does anyone know what causes this and how it can be resolved? I was under the impression that user-defined classes were supported in web2py.
As stated, just move self.info = {} into __init__().
__init__() is essentially a constructor that you are familiar with from java. It initializes an instance object of that class when called. I haven't used Java in some time, but I don't think you should be be declaring class variables outside of your constructor there either.
self is an argument that all methods within a class in python must receive as their first argument. So your getters and setters are also not going to work if you try them; they must be:
def setInfo(self, info) and def getInfo(self)
When you create an object, like this:
device1 = device_info()
it calls __init()__, passing device1 as self. Then, whenever you use that object, such as
device1.setInfo(newInfo), you can think of the method in the class' context being called as setInfo(device1, newInfo), since device1 is self, or the current instance of the device_info object in use.
You also don't need the object argument at the class definition. What do you expect that to do?
Edit: Actually, don't move self.info = {} into __init__(), just get rid of it. You already have self.info = info in __init__(). You don't need to initialize variables like that in Python like you do in Java. Creating an empty dict and then setting it to another dict without any use is redundant.
I don't think it is because of the scope of the function, but I get a
Unresolved reference at get_all_predicates(examples).count(predicate_list[0])
inside get_entropy_of_attributes(examples, predicate_list) function in my class Tree:
class Tree:
def get_examples(examples, attributes):
for value in examples:
yield dict(zip(attributes, value.strip().replace(" ", "").split(',')))
def get_all_predicates(examples):
return [d['Predicate'] for d in examples]
def get_entropy_of_attributes(examples, predicate_list):
get_all_predicates(examples).count(predicate_list[0])
return 0
examples = list(get_examples(all_examples, name_of_attributes))
predicate_list = list(set(get_all_predicates(examples)))
get_entropy_of_attributes(examples, predicate_list)
all_examples is a list of dictionary and name_of_attributes is a list, that holds values imported from a text file.
all_examples = [{'P_Length': '1.4', 'P_Width': '0.2', 'Predicate': 'I-setosa', 'Sepal_Width': '3.5', 'S_Length': '5.1'}, ...]
name_of_attributes = ["Check","P-Width"]
Any help?
Classes do not have scopes, only namespaces. This means that functions defined within them cannot see other class variables automatically.
class Foo(object):
var = 1 # lets create a class variable
def foo():
print(var) # this doesn't work!
To access a class variable, you need use attribute syntax: either Foo.var (to access via the class) or, if you're writing an instance method, with self.var (to access via the current instance, which will be passed in as the first argument).
class Bar(object):
var = 1
def bar1():
print(Bar.var) # works
def bar2(self):
print(self.var) # also works, if called on an instance, e.g. `Bar().bar2()`
With this kind of setup you can almost fix your current code (but not quite).
def get_entropy_of_attributes(examples, predicate_list):
Tree.get_all_predicates(examples).count(predicate_list[0]) # name the class
return 0
If you call this after the class is fully initialized, it will work without any exceptions (though it's implementation seems a bit nonsensical). However, it doesn't work when you call it to define a class variable, as your current code does. That's because the class object is only created and bound to the class name after all of the class body has been run.
I think the fix for that is probably to redesign your class in a more conventional way. Rather than having class variables set up based on various globals (like all_examples), you should probably create instances of your class by passing in arguments to the constructor and making the other variables you calculate from them instance attributes. I'd try to write it out, but frankly I don't understand what you're doing well enough.
If you want to call class methods, you have to call them with self, e.g.
class myClass:
def __init__(self):
pass
def get_all_predicates(self):
print('asd')
def do_something(self):
self.get_all_predicates() # working
get_all_predicates() # → Unresolved reference
test = myClass()
test.do_something()
See this link for examples for Python classes.
While researching about python class attribute and instance attribute, I came to know that it's not possible to create object attribute outside object methods (or may be class method). Like code below will generate an "NameError" in python.
class test(object):
def __init__(self):
self.lst = []
self.str = 'xyz'
Why python doesn't allow this? I'm not questioning language creator's decision, but any reason behind this. Like, is it technically incorrect or any other disadvantage of this behavior.
You are defining a class, so there is no instance to point to outside methods. Drop the `self:
class test(object):
def __init__(self):
self.lst = []
str = 'xyz'
self points to the instance, not the class. You either need to create an instance and assign directly to attributes (test().str = 'xyz') or you need to be inside a method (when self can actually refer to an instance).
self is not a special name in python, you could use \
class test(object):
def __init__(foo):
foo.lst = []
If you want. Every method of a class gets the instance explicitly passed to it as the first parameter, you can call it whatever you want. Trying to access a parameter outside the scope of the method obviously won't work.
I was wondering if there is anything wrong (from a OOP point of view) in doing something like this:
class Foobar:
foobars = {}
def __init__(self, name, something):
self.name = name
self.something = something
Foobar.foobars[name] = self
Foobar('first', 42)
Foobar('second', 77)
for name in Foobar.foobars:
print name, Foobar.foobars[name]
EDIT: this is the actual piece of code I'm using right now
from threading import Event
class Task:
ADDED, WAITING_FOR_DEPS, READY, IN_EXECUTION, DONE = range(5)
tasks = {}
def __init__(self, name, dep_names, job, ins, outs, uptodate, where):
self.name = name
self.dep_names = [dep_names] if isinstance(dep_names, str) else dep_names
self.job = job
self.where = where
self.done = Event()
self.status = Task.ADDED
self.jobs = []
# other stuff...
Task.tasks[name] = self
def set_done(self):
self.done.set()
self.status = Task.DONE
def wait_for_deps(self):
self.status = Task.WAITING_FOR_DEPS
for dep_name in self.dep_names:
Task.tasks[dep_name].done.wait()
self.status = Task.READY
def add_jobs_to_queues(self):
jobs = self.jobs
# a lot of stuff I trimmed here
for w in self.where: Queue.queues[w].put(jobs)
self.status = Task.IN_EXECUTION
def wait_for_jobs(self):
for j in self.jobs: j.wait()
#[...]
As you can see I need to access the dictionary with all the instances in
the wait_for_deps method. Would it make more sense to have a global variable
instead of a class field? I could be using a wrong approach here, maybe that
stuff shouldn't even be in a method, but it made sense to me (I'm new to OOP)
Yes. It's bad. It conflates the instance with the collection of instances.
Collections are one thing.
The instances which are collected are unrelated.
Also, class-level variables which get updated confuse some of us. Yes, we can eventually reason on what's going on, but the Standard Expectation™ is that state change applies to objects, not classes.
class Foobar_Collection( dict ):
def __init__( self, *arg, **kw ):
super( Foobar_Collection, self ).__init__( *arg, **kw ):
def foobar( self, *arg, **kw ):
fb= Foobar( *arg, **kw )
self[fb.name]= fb
return fb
class Foobar( object ):
def __init__( self, name, something )
self.name= name
self.something= something
fc= Foobar_Collection()
fc.foobar( 'first', 42 )
fc.foobar( 'second', 77 )
for name in fc:
print name, fc[name]
That's more typical.
In your example, the wait_for_deps is simply a method of the task collection, not the individual task. You don't need globals.
You need to refactor.
I don't suppose that there's anything wrong with this, but I don't really see how this would be sensible. Why would you need to keep a global variable (in the class, of all places) that holds references to all the instances? The client could just as easily implement this himself if he just kept a list of his instances. All in all, it seems a little hackish and unnecessary, so I'd recommend that you don't do it.
If you're more specific about what you're trying to do, perhaps we can find a better solution.
This is NOT cohesive, as well as not very functional, you want to strive to get your objects as far from the 'data-bucket' mindset as possible. The static object collection is not going to really gain you anything, you need to think WHY do you need all the objects in the collection and think about creating a second class whose responsibility is to manage and be queried for all the Foobars in the system.
Why would you want to do this?
There are several problems with this code. The first is that you have to take care of deleting instances -- there will always be a reference to each Foobar instance left in Foobar.foobars, so the garbage collector will never garbage collect them. The second problem is that it won't work with copy and pickle.
But apart from the technical problems, it feels like a wrong design. The purpose of object instances is hiding state, and you make them see each other.
From a OOP point of view there's nothing wrong with it. A class is an instance of a metaclass, and any instance can hold any kind of data in it.
However, from an efficiency point of view, if you don't eventualy clean up the foobars dict on a long running Python program, you are having potential memory leak.
No one has mentioned the potential problem this might have if you later derive a subclass from Foobar which could happen if the base class __init__() function is called from the derived class's __init__(). Specifically whether you want all the subclass instances to be sored in the same place as those of the base class -- which of course depend on why you're doing this.
It's a solvable problem but something to consider, and perhaps to code for, up front in the base class.
I needed multiple Jinja environments in an app engine application:
class JinjaEnv(object):
""" Jinja environment / loader instance per env_name """
_env_lock = threading.Lock()
with _env_lock:
_jinja_envs = dict() # instances of this class
def __init__(self, env_name):
self.jinja_loader = ..... # init jinja loader
self.client_cache = memcache.Client()
self.jinja_bcc = MemcachedBytecodeCache(self.client_cache, prefix='jinja2/bcc_%s/' % env_name, timeout=3600)
self.jinja_env = self.jinja_loader(self.jinja_bcc, env_name)
#classmethod
def get_env(cls, env_name):
with cls._env_lock:
if env_name not in cls._jinja_envs:
cls._jinja_envs[env_name] = JinjaEnv(env_name) # new env
return cls._jinja_envs[env_name].jinja_env
#classmethod
def flush_env(cls, env_name):
with cls._env_lock:
if env_name not in cls._jinja_envs:
self = cls._jinja_envs[env_name] = JinjaEnv(env_name) # new env
else:
self = cls._jinja_envs[env_name]
self.client_cache.flush_all()
self.jinja_env = self.jinja_loader(self.jinja_bcc, env_name)
return self.jinja_env
Used like:
template = JinjaEnv.get_env('example_env').get_template('example_template')