I'm stumped on a python problem. I'm writing a program that receives a command from Scratch (MIT) and then should create a new object, in this case named PiLight. The object only need to be created when the command is received so it doesn't have to loop, just be able to executed repeatedly and have the number increment each time it is executed.A list will not work for me due to the requirements of the program and talking between Scratch. I'm trying to figure out a way for the constructor, once initialized, to print out a statement something like
class Newpilight:
def __init__(self):
print "Pilight" + pilnumber + " created"
pilnumber should be 1 for 1st object, 2 for 2nd, etc
From there I need the creation of the object to change the number in the name of the object as well
PiLight(PiLnumber) = Newpilight()
I tried messing around with for loops but just ended up making more of a mess
Use number generator as class variable
from itertools import count
class NewPilight(object):
nums = count()
def __init__(self):
self.num = self.nums.next()
print "Pilight {self.num} created".format(self=self)
Then using in code:
>>> pl1 = NewPilight()
Pilight 0 created
>>> pl2 = NewPilight()
Pilight 1 created
>>> pl3 = NewPilight()
Pilight 2 created
>>> pl3.num
2
The trick is to have the nums (what is actually a generator of numbers, not list of numbers) as class property and not property of class instance. This way it is globally shared by all class instances.
class NewPilight:
def __init__(self, number):
self.number = number
print "Pilight" + number + " created"
for x in range(5):
NewPilight(x)
if you need to keep objects:
all_pilights = []
for x in range(5):
all_pilights.append( NewPilight(x) )
and now you have access to objects as
print all_pilights[0].number
print all_pilights[1].number
print all_pilights[2].number
class NewPiLight(object):
global_pilnumber = 0 # Since this is on the class definition, it is static
def __init__(self):
print "Pilight %s created" % NewPiLight.global_pilnumber
self.pilnumber = NewPiLight.global_pilnumber # Set the variable for this instance
NewPiLight.global_pilnumber += 1 # This increments the static variable
Related
I have a simple class that stores simple data. The class is as follows.
class DataFormater:
def __init__(self, N, P, K, price):
self.N = N
self.P = P
self.K = K
self.price = price
The code that calls this class is
from DataFormater import DataFormater
#global variables
ObjectList = [0,1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50]
ObjectListCounter = 0
# main
print "enter you N-P-K values, followed by a coma, then the price"
print "example ----> 5 5 5 %50 "
print "return as many values as you want to sort, then enter, 'done!' when done."
while True:
RawData = raw_input()
if RawData == 'done!':
break
else:
ObjectList[ObjectListCounter] = DataFormater
ObjectList[ObjectListCounter].N = int(RawData[0])
# very simple test way of putting first indice in ObjectList[ObjectListCounter].N
ObjectListCounter += 1
print ObjectList[0].N
print ObjectList[1].N
My idea is that ObjectList[0] would create that object '1' that I could call with 1.N
But, when I call these, it seems that I have overwritten the previous instances.
this is what prints...
return as many values as you want to sort, then enter, 'done!' when done.
12
1
done!
1
1
Thanks so much! And I know that my post is messy, I don't exactly know how to make it more "pretty"
So, it looks like you are assigning the actual class (instead of an instance of the class) in your loop. Where you do this:
ObjectList[ObjectListCounter] = DataFormater
I think what you actually want is this
ObjectList[ObjectListCounter] = DataFormater(...insert args here....)
EDIT to address the comments:
Your class init method looks like this:
def __init__(self, N, P, K, price):
That means that to create an instance of your class, it would look like this:
my_formater = DataFormater(1, 2, 3, 4)
You would then be able to access my_formater.N which would have a value of 1.
What you are trying to do instead is access a CLASS level attribute, DataFormater.N. This is generally used in situations where you have a constant variable that does not change between instances of the class. For example:
class DataFormater():
CONSTANT_THING = 'my thing that is always the same for every instance'
You would then be able to access that variable directly from the class, like this:
DataFormater.CONSTANT_THING
I hope that clears things up.
Is it possible to have serializable static class variables or methods in python?
As an example suppose, I have the following code snippet:
import pickle
class Sample:
count = 0 # class variable
def __init__(self, a1=0, a2=0):
self.a = a1
self.b = a2
Sample.count += 1
#MAIN
f = open("t1.dat", "wb")
d = dict()
for i in range(10):
s = Sample(i, i*i)
d[i] = s
pickle.dump(d,f)
print "Sample.count = " + str(Sample.count)
f.close()
The output is:
Sample.count = 10
Now, I have another reader program similar to above:
import pickle
class Sample:
count = 0 # class variable
def __init__(self, a1=0, a2=0):
self.a = a1
self.b = a2
Sample.count += 1
#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
print "Sample.count = " + str(Sample.count)
The output is:
Sample.count = 0
My question is:
How do I load the class variable from my file? In other words, how do I serialize a class variable? If directly not possible, is there any alternative? Please suggest.
Since class variable cannot be picked, as an alternative, I have used the code snippet in main part when reading from the file as below:
#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
Sample.count = len(d.values())
print "Sample.count = " + str(Sample.count)
The output is now:
Sample.count = 10
Is it acceptable solution? Any other alternative?
Quoting the section on "What can be pickled and unpickled?"
Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply. Note that none of the class’s code or data is pickled, so in the following example the class attribute attr is not restored in the unpickling environment:
class Foo:
attr = 'a class attr'
picklestring = pickle.dumps(Foo)
So because attr, or in your case count, is part of the class definition, it never gets pickled. In your 'write' example, you're printing Sample.count which does exist but is not pickled in the first place.
You could store Sample.count in each instance as _count and put Sample.count = self._count. But remember that since your d is a dict, they may unpickle in any order. So essentially this won't work.
You'll need to add __setstate__ to your class customize the way it pickles and put in some flag value (like _count) which you then manipulate (via whatever logic works consistently) in __getstate__. (Edit: doesn't help with the given problem unless you store count in a global variable and access that in getstate and manipulate further each time an object is unpickled.)
Another potential workaround but yuck: Add a variable to your dict d so that it also gets pickled. When you read it back, restore with Sample.count = d['_count']. So before pickle.dump(d,f) when you pickle, do d['_count'] = Sample.count.
Important caveat: This is not actually allowing you to pickle Sample.count since what you're actually pickling (d) is a dictionary of Samples.
Edit: The Sample.count = len(d.values()) which you've put as a workaround is very specific to your use case and not to class attr's in general.
If I import a class and rename it by subclassing, it's fairly simple to find the new class name:
>>> from timeit import Timer
>>> class Test(Timer):
... pass
...
>>> test = Test()
>>> test.__class__.__name__
'Test'
However, if I alias the class as I import it, it retains the name from its host module:
>>> from timeit import Timer as Test2
>>> test2 = Test2()
>>> test2.__class__.__name__
'Timer'
Later, I want to provide user-facing output which is aware of the name they've given the class in their namespace. Consider:
def report_stats(timer):
print("Runtime statistics for %s:" % timer.__class__.__name__)
...
Is there a way to get a string reading "Test2", short of iterating over variables in the namespace to test for an exact match?
There's a really terrible answer to my own question; I won't be accepting this since it's probably pretty fragile (I only tested for a limited set of call circumstances). I mostly just hunted this down for the challenge; I will most likely be using something more durable for my actual use case.
This assumes we have access to the init function of the class we're trying to import as blah, and some sort of persistent external data store, at least for more complicated edge cases:
import inspect, dis
class Idiom(object):
description = None
alias = None
def __init__(self, desc):
global data_ob
self.description = desc
if self.__class__.__name__ == 'Idiom':
#cheat like hell to figure out who called us
self.alias = data_ob.name_idiom(inspect.currentframe().f_back)
else:
self.alias = self.__class__.__name__
class DataOb(object):
code = None
locations = {}
LOAD_NAME = 101
codelen = None
def name_idiom(self, frame):
if not self.code:
self.code = frame.f_code
self.codelen = len(self.code.co_code)
self.locations = {y:x for x, y in dis.findlinestarts(self.code)}
target_line = frame.f_lineno
addr_index = self.locations[target_line]+1
name_index = self.code.co_code[addr_index]
# there's a chance we'll get called again this line,
# so we want to seek to the next LOAD_NAME instance(101)
addr_index += 1
while addr_index < self.codelen:
if self.code.co_code[addr_index] == self.LOAD_NAME:
self.locations[target_line] = addr_index
break
addr_index += 1
return self.code.co_names[name_index]
The short explanation of how this works is:
we look up the previous frame from the init function
obtain the code object
find bytecode locations for the start of every line in the code
use the line-number from the frame to grab the bytecode location for the start of that line
locate a LOAD_NAME indicator in the bytecode for this line (I don't really follow this; my code assumes it'll be there)
look in the next bytecode position for an index which indicates which position in the code.co_names tuple contains the "name" of the LOAD_NAME call
From here we can do something like:
>>> from rabbit_hole import Idiom as timer_bob
>>> with timer_bob("down the rabbit hole"):
... waste_some_time = list(range(50000))
...
timer_bob: down the rabbit hole
runtime: 0:00:00.001909, children: 0:00:00, overhead: 0:00:00.001909
I'm trying to implement trie in python. I'm using dictionaries+classes instead of lists (I know it's not optimal, but I'm just trying to make it work at all).
After debugging I found out that each layer has all letters in dictionary. I cannot understand why.
Here is my code (implementation is 100% most basic, straightforward):
class lttr:
finish = 0
pointers = {} #for letters to reference class instance
eps = lttr()
def add(word):
global eps
last = eps
for ind,x in enumerate(word):
if last.pointers.get(x,None):
last = last.pointers[x]
else:
last.pointers[x] = lttr()
last=last.pointers[x]
last.finish=1
def lookup(word):
global eps
last=eps
for ind,x in enumerate(word):
if last.pointers.get(x,None):
last=last.pointers[x]
else:
return False
return bool(last.finish)
add("pear")
print lookup("ar") #prints True ... but why?
I'm guessing you intended for each individual lttr instance to have its own unique values for finish and pointers. In which case, you need to declare them as attributes of self inside __init__, rather than just defining them at the class scope.
class lttr:
def __init__(self):
self.finish = 0
self.pointers = {} #for letters to reference class instance
Now your script will print False as expected.
Your lttr class has class variables, but you want instance variables. Class variables are unique across all instances of that class, so you only have one pointers object.
class lttr:
finish = 0
pointers = {} #for letters to reference class instance
What you want is
class lttr:
def __init__(self):
self.finish = 0
self.pointers = {}
and that works as expected.
I have created a simple renaming script but I would like to ask for some advice so that I can refine the coding as well as honing my python scripting. Below is a small portion of code for now...
Though this may not be an issue in my point of view, but other than the two functions I have stated below, I have came to realize that almost all my functions, they contains objects = cmds.ls(selection=True) Though I do not mind retyping over and over again but I do believe there is a better way to rectify this problem.
However, when I tried to make them global before the class function, it is able to run until when I tired to execute one of the functions, it prompts an error saying that global name 'objects' is not defined or 'objects are not defined' etc.
Pertaining to that, any suggestions?
class mainWindow(QDialog):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.resize(300,225)
self.initUI()
self.createConnections()
def searchReplace(self):
wordSearch = str(self.searchTxt.text())
wordReplace = str(self.replaceTxt.text())
objCnt = cmds.ls(sl=True, sn=True)
if len(objCnt) == 0:
self.searchTxt.clear()
self.replaceTxt.clear()
cmds.warning('Nothing is selected')
else:
for wordString in sorted(objCnt):
if wordSearch in wordString:
newWordString = wordString.replace(wordSearch, wordReplace)
cmds.rename(wordString, newWordString)
self.searchTxt.clear()
self.replaceTxt.clear()
print '%s' %wordString + " has changed to : " + "%s" %newWordString
def addPrefix(self):
objects = cmds.ls(selection=True)
pfx = str(self.prefixTxt.text())
for item in objects:
if pfx == "":
cmds.warning('No prefix values in the field')
else:
cmds.rename(item, pfx + "_" + item)
self.prefixTxt.clear()
print 'Prefix added: %s_' %pfx
def addSuffix(self):
objects = cmds.ls(selection=True)
sfx = str(self.suffixTxt.text())
for item in objects:
cmds.rename(item, item + "_" + sfx)
self.suffixTxt.clear()
print 'Suffix added: _%s' %sfx
def numPadding(self):
objects = pm.ls(selection=True)
num = self.numTxt.text()
padding = self.paddingTxt.text()
if num != "" and padding !="":
try:
for currentWordStr in objects:
pad = ("%%0%ii" % int(padding)) % int(num)
newWordStr = currentWordStr.rename(currentWordStr.name() + "_" + pad)
except Exception:
self.numTxt.clear()
self.paddingTxt.clear()
cmds.warning('Input numerical values only')
else:
cmds.warning('Entries of Num or Padding are empty')
def selectHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) == 0:
cmds.warning('Nothing is selected')
else:
objHierarchy = cmds.listRelatives(ad=True, type='transform', fullPath=True)
cmds.select(sel, objHierarchy)
def clearHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) != 0 :
objHierarchy = cmds.select(clear=True)
else:
cmds.warning('Selection is empty. Nothing to be cleared')
All right, I think I understand what you tried, going to take a shot at an answer.
First, take a look at the following posts, should get you up to speed on globals:
Using global variables in a function other than the one that created them (great, succinct summary)
Variable scope outside of classes (example with classes)
So, first off, you don't need to use the global keyword when first declaring objects outside of the class definition. So, instead of:
global objects
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
You would do:
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
Then, your functions can just refer to "objects". If you need to WRITE to objects from within your functions in the class, then you need to first use the global keyword (this code assumes objects was defined before the class):
def my_method(self):
global objects
objects = some_function()
That said, I'm not 100% sure how the above code is being invoked, so it's possible that something else is causing "objects" to be undefined.
You might be better served with a class attribute here. You could do this:
class mainWindow(QDialog):
objects = cmds.ls(selection=True)
def my_func(self):
for item in self.objects:
do_stuff()
Keep in mind that objects would be the same for all instances of mainWindow, and any updates to objects in one instance will affect all other instances. That should be fine from what I can tell, but you should definitely become familiar with instance vs. class vs. module.
Hope that helps!
UPDATE: Whoops, changed the class attribute in one place, but not the other in the last example. Updated the example, it should make way more sense now.