Printing from within properties - python

I'm trying to make a robotics kit. Its designed to be simple so I'm using properties so when the users change a parameter the property method sends the serial command which controls motors/ servos/whatever.
This is the code at the moment, directly from a previous question I asked on here.
class Servo(object):
def __init__(self, which_servo, angle = 0):
self._angle = angle;
self._servo_no = which_servo
def get_angle(self):
return self._angle
def set_angle(self, value):
self._angle = value
print "replace this print statement with the code to set servo, notice that this method knows the servo number AND the desired value"
def del_angle(self):
del self._angle
angle = property(get_angle, set_angle, del_angle, "I'm the 'angle' property.
this is then initialized as such:
class robot(object):
def __init___(self):
self.servos = [Servo(0), Servo(1), Servo(2), Servo(3)]
Now, this works in the respect that it does change the variable through the getter and setter functions, however the prints in the getter and setter never is printed, thus if I replace it with a serial command I assume it won't do anything either, can anyone shed any light on this?
Thanks
Update: Thanks for the help using the servo file this is whats happened, there are three scenarios the first works and by extension I would have assumed the next two preferable scenarios would work but they don't any ideas?
This works
import servo
class Robot(object):
def __init__(self):
self.servos = [servo.Servo(0, 0), servo.Servo(1,0), servo.Servo(2,0)]
R = Robot()
R.servos[1].angle = 25
This does not:
import servo
class Robot(object):
def __init__(self):
self.servos = [servo.Servo(0, 0), servo.Servo(1,0), servo.Servo(2,0)]
R = Robot()
left_servo = R.servos[1].angle
left_servo = 25
Neither does this
import servo
class Robot(object):
def __init__(self):
self.servos = [servo.Servo(0, 0).angle, servo.Servo(1,0).angle, servo.Servo(2,0).angle]
R = Robot()
R.servo[1] = 25

Using the preferred decorator syntax for properties, this works fine. It'll also help you avoid issues like this in the future
class Servo(object):
def __init__(self, which_servo, angle = 0):
self._angle = angle;
self._servo_no = which_servo
#property
def angle(self):
return self._angle
#angle.setter
def angle(self, value):
self._angle = value
print "replace this print statement with the code to set servo"
#angle.deleter
def angle(self):
del self._angle
Seeing as your indentation is off here, I believe this is likely an issue of indentation in your source. This should work as well if you really want to use the old property function:
class Servo(object):
def __init__(self, which_servo, angle = 0):
self._angle = angle;
self._servo_no = which_servo
def get_angle(self):
return self._angle
def set_angle(self, value):
self._angle = value
print "replace this print statement with the code to set servo"
def del_angle(self):
del self._angle
angle = property(get_angle, set_angle, del_angle,"I'm the 'angle' property.")
Both of these work successfully for me (inside a file called servo.py)
>>> import servo
>>> s = servo.Servo(1, 2)
>>> s.angle
2
>>> s.angle = 3
replace this print statement with the code to set servo
EDIT
To address your new issues. When you assign R.servos[1].angle to left_servo, its not creating a reference to the servos angle, it's just setting left_servo to whatever the angle is. When you reassign 25 to it, you're not assigning to the angle you're assigning to the left_servo.
On the second one, I'm assuming you meant R.servos and not R.servo which should be raising an AttributeError. But the real problem as I see it, is you should be saying R.servos[1].angle = 25 and you're omitting the .angle.
To (attempt to) put it simply: When you use the = operator, you are changing where a name refers to, not what it refers to.
>>> x = 1
>>> x = 2
the second assignment does not overwrite the 1 in memory with a 2, it just changes where x refers to. So if I did something like
>>> x = 1
>>> y = x
>>> y = 2
>>> print x
1
the output is 1 because your are telling y to refer to the same place that x refers. Changing y to 2 changes where y refers to, it does not change the 1 already in memory.

Related

How do I get python to read that () is 0?

I'm having issues trying to pass an empty parameter can someone explain to me why my code isn't working. I have a math test file that goes through my math library file but my lib file can't read the () code. When I run the code it says init() missing 1 required positional argument: 'y'
import MathLib as math
math test:
if __name__ == '__main__':
math_obj1 = math.MyMathLib(2.0)
math_obj2 = math.MyMathLib(-0.5)
math_obj3 = math.MyMathLib() # this should give 0.0
print("Math obj1 value = ",math_obj1.get_curr_value() )
print("Math obj2 value = ",math_obj2.get_curr_value() )
print("Math obj3 value = ",math_obj3.get_curr_value() )
import math
class MyMathLib:
def __init__(self, y,):
self.y = y
if self == None:
value == 0.0
As posted, your definition of the __init__() function has y as a required argument.
If you want it to be optional and have a default value of zero, then write it this way:
class MyMathLib:
def __init__(self, y=0.0):
The self variable isn't actually a passable parameter in class methods (I recommend you take another look at python classes). The first (and only) passable parameter in your init function is y. Since y has no default variable, you must pass a value for y, or give it a default value:
def __init__(self, y=0.0):
self.y = y
Also I'm not sure what you're trying to achieve with this line, it makes no sense:
if self == None:
value == 0.0
value is only local to the init function, maybe you meant self.value? Even then, self will never be None (unless you assign self = None within the method), so the statement will never trigger. Ontop of that, you've used a double == instead of =.
You have to set default value in __init__
def __init__(self, y=0.0):
self.y = y
and then you don't have to check None
Or using None
def __init__(self, y=None):
self.y = y
if self.y is None:
self.y = 0.0
It can be useful if you want to recognize if someone used MyMathLib() or MyMathLib(0.0)
That is because your __init__ requires two arguments instead of one. Instead of doing this, you can pass a default variable like #Jay Mody's answer. And also:
self == None will never be true because self always passes in a value y.
Here is another way you can do it:
class MyMathLib:
def __init__(self):
self.y = 0.0
def passNumber(y):
self.y = y
As you can see, if the number is passed using passNumber, that means that the number isn't 0.0. This is another way to do it.

Python object is being referenced by an object I cannot find

I am trying to remove an object from memory in python and I am coming across an object that it is not being removed. From my understanding if there is no references to the object the garbage collector will de-allocate the memory when it is run. However after I have removed all of the references if I run
bar = Foo()
print gc.get_referrers(bar)
del bar
baz = gc.collect()
print baz
I get a reply of
[< frame object at 0x7f1eba291e50>]
0
So how come does it not delete the object?
I get the same reply for all of the instances of objects if i do
bar = [foo() for i in range(0, 10)]
for x in range(0,len(bar))
baz = bar[x]
del bar[x]
print gc.get_referrers(baz)
How do I completely remove all referrers from an object/any idea what the frame object that is on all is?
I thought it would be the object frame(?) that contains a list of all objects in the program but I have not been able to confirm that/find a way to rid objects from being referenced by said mystical(to me) object fram.
Any help would be greatly appreciated
Edit:
Okay I rewrote the code to the simple form pulling out everything except the basics
import random, gc
class Object():
def __init__(self):
self.n=None
self.p=None
self.isAlive=True
def setNext(self,object):
self.n=object
def setPrev(self, object):
self.p=object
def getNext(self):
return self.n
def getPrev(self):
return self.p
def simulate(self):
if random.random() > .90:
self.isAlive=False
def remove(self):
if self.p is not None and self.n is not None:
self.n.setPrev(self.p)
self.p.setNext(self.n)
elif self.p is not None:
self.p.setNext(None)
elif self.n is not None:
self.n.setPrev(None)
del self
class Grid():
def __init__(self):
self.cells=[[Cell() for i in range(0,500)] for j in range(0,500)]
for x in range(0,100):
for y in range(0,100):
for z in range(0,100):
self.cells[x][y].addObject(Object())
def simulate(self):
for x in range(0,500):
for y in range(0,500):
self.cells[x][y].simulate()
num=gc.collect()
print " " + str(num) +" deleted today."
class Cell():
def __init__(self):
self.objects = None
self.objectsLast = None
def addObject(self, object):
if self.objects is None:
self.objects = object
else:
self.objectsLast.setNext(object)
object.setPrev(self.objectsLast)
self.objectsLast = object
def simulate(self):
current = self.objects
while current is not None:
if current.isAlive:
current.simulate()
current = current.getNext()
else:
delete = current
current = current.getNext()
if delete.getPrev() is None:
self.objects = current
elif delete.getNext() is None:
self.objectsLast = delete.getPrev()
delete.remove()
def main():
print "Building Map..."
x = Grid()
for y in range (1,101):
print "Simulating day " + str(y) +"..."
x.simulate()
if __name__ == "__main__":
main()
gc.get_referrers takes one argument: the object whose referers it should find.
I cannot think of any circumstance in which gc.get_referrers would return no results, because in order to send an object to gc.get_referrers, there has to be a reference to the object.
In other words, if there was no reference to the object, it would not be possible to send it to gc.get_referrers.
At the very least, there will be a reference from the globals() or from the current execution frame (which contains the local variables):
A code block is executed in an execution frame. An execution frame contains some administrative information (used for debugging), determines where and how execution continues after the code block's execution has completed, and (perhaps most importantly) defines two namespaces, the local and the global namespace, that affect execution of the code block.
See an extended version of the example from the question:
class Foo(object):
pass
def f():
bar = [Foo() for i in range(0, 10)]
for x in range(0, len(bar)):
# at this point there is one reference to bar[x]: it is bar
print len(gc.get_referrers(bar[x])) # prints 1
baz = bar[x]
# at this point there are two references to baz:
# - bar refernces it, because it is in the list
# - this "execution frame" references it, because it is in variable "baz"
print len(gc.get_referrers(bar[x])) # prints 2
del bar[x]
# at this point, only the execution frame (variable baz) references the object
print len(gc.get_referrers(baz)) # prints 1
print gc.get_referrers(baz) # prints a frame object
del baz
# now there are no more references to it, but there is no way to call get_referrers
f()
How to test it properly?
There is a better trick to detect whether there are referers or not: weakref.
weakref module provides a way to create weak references to an object which do not count. What it means is that even if there is a weak reference to an object, it will still be deleted when there are no other references to it. It also does not count in the gc.get_referrers.
So:
>>> x = Foo()
>>> weak_x = weakref.ref(x)
>>>
>>> gc.get_referrers(x) == [globals()] # only one reference from global variables
True
>>> x
<__main__.Foo object at 0x000000000272D2E8>
>>> weak_x
<weakref at 0000000002726D18; to 'Foo' at 000000000272D2E8>
>>> del x
>>> weak_x
<weakref at 0000000002726D18; dead>
The weak reference says that the object is dead, so it was indeed deleted.
Okay thanks to cjhanks and user2357112 I came up with this answer
The problem being that if you run the program the gc does not collect anything after each day even though there were things deleted
To test if it is deleted I instead run
print len(gc.get_objects())
each time I go through a "day" doing this shows how many objects python is tracking.
Now with that information and thanks to a comment I tired changing Grid to
class Grid():
def __init__(self):
self.cells=[[Cell() for i in range(0,500)] for j in range(0,500)]
self.add(100)
def add(self, num):
for x in range(0, 100):
for y in range(0, 100):
for z in range(0, num):
self.cells[x][y].addObject(Object())
def simulate(self):
for x in range(0,500):
for y in range(0,500):
self.cells[x][y].simulate()
num=gc.collect()
print " " + str(num) +" deleted today."
print len(gc.get_objects())
and then calling Grid.add(50) halfway through the process. My memory allocation for the program did not increase (watching top in Bash) So my learning points:
GC was running without my knowledge
Memory is allocated and never returned to system until the the program is done
Python will reuse the memory

Why isn't self working in my code

I'm writing some code to create a toolbar that edits a map in ArcMap and I'm having some issues with getting variable values from other functions inside other classes that I'm using.
All the functions are predefined so I can't change the int arguments or the code will throw an error. I checked the dir() and none of the variables I define using self are in the functions. I don't think I've made a syntax error and the code inside the other classes works correctly.
Here is my code:
import arcpy
import math
import pythonaddins
class findingCoordinates(object):
"""Implementation for leetScripts_addin.tool (Tool)"""
def __init__(self):
self.enabled = True
self.shape = "NONE"
def onMouseDownMap(self, x, y, button, shift):
print "onMouseDowMap executing"
#this is where I declared the first two variables using self
self.x = x
self.y = y
print "Selected point is at %r, %r" % (self.x, self.y)
pass
class squareFeetInput(object):
"""Implementation for leetScripts_addin.combobox (ComboBox)"""
def __init__(self):
self.editable = True
self.enabled = True
#self.dropdownWidth = 'WWWWWW'
self.width = 'WWWWWW'
def onEditChange(self, text):
squareFeet = text
#this is the other variable I defined that I need to use later
self.buffDist = (math.sqrt(float(squareFeet))/2)
print "Square size: %r ft^2 Buffer Distance: %r ft^2" % (squareFeet,self.buffDist)
print "self.buffdist is a %r type" % self.buffDist
return self.buffDist
pass
class buildingTool(object):
"""Implementation for leetScripts_addin.button (Button)"""
def __init__(self):
self.enabled = True
self.checked = False
def onClick(self):
print "building tool is executing"
#shows im_self, but no x or y
print "%r" % dir(findingCoordinates.onMouseDownMap)
# Get arguments:
# Input point feature class
# Output polygon feature class
# Buffer distance
# Boolean type: Maintain fields and field values of the input in the output
#This is where the problem is. I can't get these values from the previous functions.
inPoints = (findingCoordinates.onMouseDownMap.x,findingCoordinates.onMouseDownMap.y)
outPolys = "U:\JackBuildingFootprints.gdb\BuildingFootprintsCopy"
bufDist = squareFeetInput.buffDist
keepFields = true
# Prepare the output based on whether field and field values are desired in the output
#
if keepFields:
# Create empty output polygon feature class that includes fields of the input
#
arcpy.CreateFeatureClass(os.path.dirname(outPolys), os.path.basename(outPolys), "POLYGON",
inPoints, "", "", inPoints)
# Create a short list of fields to ignore when moving fields values from
# input to output
#
ignoreFields = []
# Use Describe properties to identify the shapeFieldName and OIDFieldName
#
desc = arcpy.Describe(inPoints)
ignoreFields.append(desc.shapeFieldName)
ignoreFields.append(desc.OIDFieldName)
# Create a list of fields to use when moving field values from input to output
#
fields = arcpy.ListFields(inPoints)
fieldList = []
for field in fields:
if field.name not in ignoreFields:
fieldList.append(field.name)
else:
# Create empty output polygon feature class without fields of the input
#
arcpy.CreateFeatureclass(os.path.dirname(outPolys), os.path.basename(outPolys), "POLYGON",
"", "", "", inPoints)
# Open searchcursor
#
inRows = arcpy.SearchCursor(inPoints)
# Open insertcursor
#
outRows = arcpy.InsertCursor(outPolys)
# Create point and array objects
#
pntObj = arcpy.Point()
arrayObj = arcpy.Array()
for inRow in inRows: # One output feature for each input point feature
inShape = inRow.shape
pnt = inShape.getPart(0)
# Need 5 vertices for square buffer: upper right, upper left, lower left,
# lower right, upper right. Add and subtract distance from coordinates of
# input point as appropriate.
for vertex in [0,1,2,3,4]:
pntObj.ID = vertex
if vertex in [0,3,4]:
pntObj.X = pnt.X + bufDist
else:
pntObj.X = pnt.X - bufDist
if vertex in [0,1,5]:
pntObj.Y = pnt.Y + bufDist
else:
pntObj.Y = pnt.Y - bufDist
arrayObj.add(pntObj)
# Create new row for output feature
#
feat = outRows.newRow()
# Shift attributes from input to output
#
if keepFields == "true":
for fieldName in fieldList:
feat.setValue(fieldName, inRow.getValue(fieldName))
# Assign array of points to output feature
#
feat.shape = arrayObj
# Insert the feature
#
outRows.insertRow(feat)
# Clear array of points
#
arrayObj.removeAll()
# Delete inputcursor
#
del outRows
pass
What am I doing wrong? Is this one of the rare occasions where I should use a global variable? Why is the directory not showing the variables I defined using self?
Edit:
I made this post a while ago and I just wanted to clear some things up now that I know more.
First:
This is code that is designed to be use with python_add_in. Python add in creates a toolbar based on some parameters you give it when you set it up, and whatever python code you put into a template it makes as a result of those parameters. That essentially means that all of the classes in the script above are events that occur when buttons and other toolbar objects are clicked or used in the toolbar.
Second:
The solution to this problem actually isn't in the accepted answer, my bad.
The root cause of the problem is that I was using class names that I declared in my script, findingCoordinates for example. python_add_in doesn't recognize these class names as the names of the classes it expects to receive based on the template you fill out before you start coding.
With that in mind, the issue was that I was trying to call classes that just didn't exist as far as python_add_in was concerned. The solution is to just go ahead and use the class names python_add_in tool expects you to use. These names are in the docstring located below the class definition so where I have findingCoordinates I should have Tool.
I hope this helps.
self refers to an instance of the class that you've defined, so to access those values, you need to create an instance of the class, call the method, and then access the values from the instance.
For example:
In [9]: %paste
class findingCoordinates(object):
"""Implementation for leetScripts_addin.tool (Tool)"""
def __init__(self):
self.enabled = True
self.shape = "NONE"
def onMouseDownMap(self, x, y, button, shift):
print "onMouseDowMap executing"
#this is where I declared the first two variables using self
self.x = x
self.y = y
print "Selected point is at %r, %r" % (self.x, self.y)
pass
## -- End pasted text --
In [10]: f = findingCoordinates()
In [11]: f.onMouseDownMap(x=1, y=2, button="button", shift="shift")
onMouseDowMap executing
Selected point is at 1, 2
In [12]: f.x
Out[12]: 1
In [13]: f.y
Out[13]: 2
EDIT: It seems like you've had some confusion about scoping/namespaces as well. There's no x or y defined globally; they just exist within the class instances. That will also allow you to have separate x and y values for different instances of the class.
In [14]: x
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-14-401b30e3b8b5> in <module>()
----> 1 x
NameError: name 'x' is not defined
In [15]: y
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-15-009520053b00> in <module>()
----> 1 y
NameError: name 'y' is not defined
In [16]: g = findingCoordinates()
In [17]: g.onMouseDownMap(100,200,0,0)
onMouseDowMap executing
Selected point is at 100, 200
In [18]: f.x, f.y
Out[18]: (1, 2)
In [19]: g.x, g.y
Out[19]: (100, 200)

Having trouble calling Classes in Python

I'm trying to teach myself how to use classes and I'm trying an example I found in a book that asks you to create two classes and then print out some information. Here is my code:
import math
import turtle
import urllib.request
class Shape:
def __init__(self,x=0,y=0):
self.x = x
self.y = y
def calc_area(self):
pass
def calc_perim(self):
pass
def get_shape_type(self):
return "s"
def to_string(self):
return "%s %f %f" % (self.get_shape_type(), self.x, self.y)
def get_draw_params(self):
return [self.x, self.y]
class Circle(Shape):
def __init__(self,x=0,y=0,rad=0):
super().__init__(x,y)
self.radius = rad
def calc_area(self):
area = math.pi * self.radius * self.radius
return area
def calc_perim(self):
perim = 2 * math.pi * self.radius
return perim
def calc_circumference(self):
return self.calc_perim()
def get_shape_type(self):
return "c"
def to_string(self):
return "%s %f %f %f" % (super().to_string(), self.radius, self.calc_area(),self.calc_perim())
def get_draw_params(self):
result = super().get_draw_params()
result.extend([self.radius])
return result
cir = Circle(0,0,150)
print(cir)
When I try to run it, it prints this:
<__main__.Circle object at 0x103d19ef0>
I'm not sure what I'm doing wrong when I'm calling the Circle class. I was hoping that after putting in the values that the init function asks for, there would be some data to print out. Any help would be greatly appreciated.
Try calling the to_string() method that you added to your classes:
>>> cir = Circle(0,0,150)
>>> print(cir)
<__main__.Circle object at 0x7fba2851b400>
>>> print(cir.to_string())
c 0.000000 0.000000 150.000000 70685.834706 942.477796
If you are wanting a customised string representation, try adding __unicode__() and/or __str__() methods to your classes:
def __str__(self):
return self.to_string()
Now you can do this:
>>> c = Circle(0,0,150)
>>> print(c)
c 0.000000 0.000000 150.000000 70685.834706 942.477796
It's actually quite right, this is how python prints your object,
if you want your print(object) print something else, define __str__ method in your class, something like this (it should return an string):
class Circle(Shape):
# your stuff
# ...
def __str__(self):
return "radius: " + self.radius
cir = Circle(0,0,150)
print(cir)
# radius: 150
There is nothing wrong with what you did. Everything is working fine.
This: <__main__.Circle object at 0x103d19ef0>
is indicating to you have you have an object of Circle. So, if you add this:
print(cir.calc_area())
You will end up getting the area result you expect.
Furthermore, a neat bit of information that can help you is if you want to find out more information about what is inside your objects, you can do this:
print(dir(cir))
This will tell you what is housed inside your 'cir' object and you will also see that your methods you created should be there as well. Always handy to find out what is available to you even when you import other modules when you dive deeper in to Python.
Documentation on dir
Well, that is the default representation of a Circle object when you print it.
Now you can call the methods of cir, like
print(cir.calc_area())

Access class variables with same function

I want to create a function within a class that can access two different members with the same function. For example in the code below, I want both of the lines below to use the 'apply' function on different variables in the class
print(state.apply(rate))
print(state.apply(wage))
I had thought if I put in a dummy variable in the function definition (called exposure), it would replace it with the variables passed to the function (rate and wage in the example below). What is the correct way of doing this in python 3?
class State():
def __init__(self):
self.rate = 0
self.wage = 0
def apply(self, exposure):
self.exposure = self.exposure - 1
return self.exposure
state = State()
rate = State.rate
wage = State.wage
print(state.apply(rate))
print(state.apply(wage))
EDIT: I had made a typo where I had State instead of state in each print statement. I have now corrected this
This would be the only way:
class State:
def __init__ (self):
self.rate = 0
self.wage = 0
def apply (self, exposure):
setattr(self, exposure, getattr(self, exposure) - 1)
return getattr(self, exposure)
>>> state = State()
>>> print(state.apply('rate'))
-1
>>> print(state.apply('wage'))
-1
>>> print(state.apply('wage'))
-2
Note that those are instance variables, so you cannot access them using the type, State, but only using the object, state.
However, I would say, that whatever you are trying, you’re doing it wrong. If you describe your actual problem, we may be able to suggest a way better solution for it instead.

Categories

Resources