How to get the name of a variable - python

I'm practicing writing classes in Python and I'm stumped on how to do something.
class GolfClub:
def __init__(self, size, distance):
self.size = size
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
return "(name of club variable) hits further"
else:
return "(name of club variable) hits further"
If I do:
club1 = GolfClub(5, 200)
club2 = GolfClub(6, 300)
club1.hits_further(club2)
How can I make the hits_further method return the name of the variable? For example, I would like it to return:
"club2 hits further"
How can I get the variable names into the method?

Traditionally, you'd give the instance a name:
class GolfClub:
def __init__(self, name, size, distance):
self.name = name
self.size = size
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
return "%s hits further" % self.name
else:
return "%s hits further" % other.name
club1 = GolfClub('Driver', 5, 200)
club2 = GolfClub('9Iron', 6, 300)
club1.hits_further(club2)
The instance itself has no possible way of knowing what name you've given to the variable containing it. So, store the name inside the instance.

Fundamentally, you can't do what you want here, because, in Python, the name is not a property of the variable. It's just a handle, which can be attached and reattached, while the object can have multiple names pointing to it, none of which is the name.
This is why others have suggested adding a name as an init parameter.

If I were to do what you were doing, I would change your class to do something like:
class GolfClub:
def __init__(self, size, distance):
self.size = size
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
return True
else:
return False
And the code to do something like:
club1 = GolfClub(5, 200)
club2 = GolfClub(6, 300)
if club1.hits_further(club2):
print("club1 hits further")
else:
print("club2 hits further")

Change the return statement to a print statement.
Add a name attribute to your class. Then do something like this:
class GolfClub:
def __init__(self, size, distance,name):
self.size = size
self.name = name
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
print self.name, "club hits further"
else:
print other.name ,"That club hits further"
club1 = GolfClub(5, 200,"c1")
club2 = GolfClub(6, 300,"c2")
club1.hits_further(club2)
As far as i know, what you wish to achieve is not really possible

In fact it can be done asking the stack
import inspect
import traceback
import sys
import re
class GolfClub:
def __init__(self, size, distance):
self.size = size
self.distance = distance
def hits_further(self, other):
call_str= traceback.extract_stack()[0][3]
m = re.search('([\w]+)\.hits_further\((.*)\)',call_str)
self_name=m.group(1)
other_name=m.group(2)
if self.distance > other.distance:
return self_name+" hits further"
else:
return other_name+" hits further"
club1=GolfClub(1,1)
club2=GolfClub(2,2)
print club1.hits_further(club2)
print club2.hits_further(club1)
The result is:
club2 hits further
club2 hits further
As asked by geogeogeo

You can make it like this:
def name(**variable):
return [x for x in variable]

Related

Objects passing objects

I'm new to python and am currently trying to use an old module to output graphs. The code below is a excerpt from the module that uses rpy to design
standard celeration charts (don't look it up).
I'm having trouble understanding how the class Element and class Vector work together.
I've been trying to pass the a element object to the vector get_elements but I'm not sure if that's what I should be doing.
Any help would be appreciated. Thanks!
class Element(object):
"""Base class for Chartshare vector elements."""
def __init__(self, offset=0, value=0):
self.offset=offset
self.value=value
self.text=''
def setText(self, value):
self.value=value
def getText(self):
return self.value
text = property(getText, setText)
class Vector(object):
"""Base class for Chartshare Vectors."""
def __init__(self, name='', color='black', linetype='o', symbol=1, clutter=0, start=0, end=140, continuous=False, debug=False):
self.name=name
self.color=color
self.linetype=linetype
self.symbol=symbol
self.start=start
self.end=end
self.elements={}
self.debug=debug
self.continuous=continuous
if not self.continuous:
for i in range(self.start, self.end+1):
self.elements[i]='NaN'
def getSymbol(self):
return self._symbol
def setSymbol(self, value):
if (type(value) == int):
if (value >= 0) and (value <= 18):
self._symbol = value
else:
raise SymbolOutOfRange, "Symbol should be an integer between 0 and 18."
elif (type(value) == str):
try:
self._symbol = value[0]
except IndexError:
self._symbol=1
else:
self._symbol = 1
symbol = property(getSymbol, setSymbol)
def getLinetype(self):
return self._linetype
def setLinetype(self, value):
if (value == 'p') or (value == 'o') or (value == 'l'):
self._linetype = value
else:
raise InvalidLinetype, "Line type should be 'o', 'p', or 'l'"
linetype = property(getLinetype, setLinetype)
def get_elements(self):
"""Returns a list with the elements of a Vector."""
retval = []
for i in range(self.start, self.end+1):
if (not self.continuous):
retval.append(self.elements[i])
else:
if (self.elements[i] != 'NaN'):
retval.append(self.elements[i])
return retval
def get_offsets(self):
"""Returns a list of the offsets of a Vector."""
retval = []
for i in range(self.start, self.end+1):
if (not self.continuous):
retval.append(i)
else:
if (self.elements[i] == 'NaN'):
retval.append(i)
return retval
def to_xml(self, container=False):
"""Returns an xml representation of the Vector."""
if (container == False):
container = StringIO.StringIO()
xml = XMLGenerator(container)
attrs = {}
attrs[u'name'] = u"%s" % self.name
attrs[u'symbol'] = u"%s" % self.symbol
attrs[u'linetype'] = u"%s" % self.linetype
attrs[u'color'] = u"%s" % self.color
xml.startElement(u'vector', attrs)
for i in range(self.start, self.end+1):
if (self.elements[i] != 'NaN'):
attrs.clear()
attrs[u'offset'] = u"%s" % i
xml.startElement(u'element', attrs)
xml.characters(u"%s" % self.elements[i])
xml.endElement(u'element')
xml.endElement(u'vector')
def render(self):
"""Plots the current vector."""
if (self.debug):
print "Rendering Vector: %s" % self.name
print self.elements
r.points(x=range(self.start, self.end+1),
y=self.elements,
col=self.color,
type=self.linetype,
pch=self.symbol)
if (self.debug):
print "Finished rendering Vector: %s" % self.name
Vector's get_elements() doesn't take any arguments. Well, technically it does. It takes self. self is syntactic sugar that lets you do this:
vec = Vector()
vec.get_elements()
It's equivalent to this:
vec = Vector()
Vector.get_elements(vec)
Since get_elements() doesn't take any arguments, you can't pass a to it. Skimming the code, I don't see a set_elements() analog. This means you'll have to modify the vector's element's dictionary directly.
vec = Vector()
vec.elements[a] = ...
print(vec.get_elements()) # >>> [a,...]
As I can see, there is no place in this code where you are assigning self.elements with any input from a function. You are only initialising it or obtaining values
Also note that the .get_elements() function doesn't have any arguments (only self, that is the object where you are calling it in), so of course it won't work.
Unless you can do something such as the following, we would need more code to understand how to manipulate and connect these two objects.
element_obj = Element()
vector_obj = Vector()
position = 4
vector_obj.elements[4] = element_obj
I got to this answer with the following: as I can see, the elements property in the Vector class is a dictonary, that when you call vector_obj.get_elements() is casted to an array using the start and end parameters as delimiters.
Unless there is something else missing, this would be the only way I could think out of adding the an element into a vector object. Otheriwse, we would need some more code or context to understand how these classes behave with each other!
Hope it helps!

TypeError: printArea() takes 0 positional arguments but 1 was given

I created a class that finds the area of a rectangle. The problem I have is that the printArea() method gives me a type error whenever I try to check an instance of the class and I don't know what is wrong.
class Rectangle:
length = 0;
breadth = 0;
def __init__(self, length, breadth):
self.length = length
self.breadth = breadth
def setLength(self, length):
self.length = length
def getLength(self):
return self.length
def setBreadth(self, breadth):
self.breadth = breadth
def getBreadth(self):
return self.breadth
def calculateArea(self):
Area = self.getLength() * self.getBreadth()
return Area
def printArea(self):
Area = self.getLength() * self.getBreadth()
print ("The area is %d" % (Rectangle.Area))
If I try x = Rectangle(7,4) and then tryx.printArea(), I get:
Traceback (most recent call last):
File "<pyshell#229>", line 1, in <module>
x.printArea()
TypeError: printArea() takes 0 positional arguments but 1 was given
TLDR
If I run your code I get AttributeError: type object 'Rectangle' has no attribute 'Area'. The code works if you change it to
def printArea(self):
area = self.getLength() * self.getBreadth()
print("The area is %d" % (area))
Longer answer
When you write Rectangle.Area, this does not give you the area of this current rectangle (which would be given by self.getArea()), instead you get the member Area of the class Rectangle (which does not exist).
With that said, you could get what you want by making area a property and calling it like this:
class Rectangle:
def __init__(self, length, breadth):
self._length = length
self.breadth = breadth
#property
def area(self):
return self.length * self.breath
def printArea(self):
print ("The area is %d" % (self.area))
Note also that get... and set... are heavily frowned upon in python, and accessing the member is totally fine. If you truly need them, you should use properties with getters and setters
class Rectangle:
def __init__(self, length, breadth):
self.length = length
self.breadth = breadth
#property
def length(self):
return self._length
#length.setter
def length(self, value):
self._length = value

error when executing same loop

I'm new to Python and I really want to understand why I get this error.
It happens in my findLargest function, while trying to execute the second for loop. The thing is that the second for loop does basically the same thing as the first one, but for some reason I get an error as I try to call on a (class)method. How can this be? Am I not allowed to have 2 for loops for same iterable in the same function?
shapeArea=shape.area()
throws:
TypeError: 'float' object is not callable
The objective of findlargest() is to loop through the set of classes twice, first in order to find the largest value(Area) while the second tries to find if there are other values that are equal.
class Shape(object):
def area(self):
raise AttributeException("Subclasses should override this method.")
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
self.area = (self.base * self.height) / 2
return self.area
def __str__(self):
return "{} with base {} and height {}".format(self.__class__.__name__, self.base, self.height)
def __eq__(self, other):
return type(other) == Triangle and self.base == other.base and self.height == other.height
class ShapeSet:
def __init__(self):
self.shape_list = []
def addShape(self, sh):
if sh not in self.shape_list:
self.shape_list.append(sh)
else:
print ("{} is already existing".format(sh.__str__()))
def __iter__(self):
return (self.shape_list)
def __str__(self):
s = ''
for shape in self.__iter__():
s+= shape.__str__() + "\n"
return s
ss = ShapeSet()
ss.addShape(Triangle(1.2,2.5))
ss.addShape(Triangle(1.4,2.5))
ss.addShape(Triangle(1.3,2.5))
ss.addShape(Triangle(1.5,2.5))
def findLargest(shapes):
maxs = None
maxA = 0.0
for shape in shapes.__iter__():
shapeArea = shape.area()
if shapeArea > maxA or maxs == None:
maxs = shape
maxA = shapeArea
maxTuple = (maxs)
for shape in shapes.__iter__():
shapeArea = shape.area()
With this:
def area(self):
self.area=(self.base*self.height)/2
return self.area
You enter the method and then immediately mask it by assigning a different name to its reference. From then on, self.area refers to that number and you can no longer access that method. Fortunately, the fix is easy: don't save a reference at all.
def area(self):
return self.base * self.height / 2
Python does not separate the names for function/method objects and for other objects. Use unique reference names for any objects you'd like to retain.
I think your problem is here:
class Triangle(Shape):
def __init__(self, base, height):
self.base=base
self.height=height
def area(self):
self.area=(self.base*self.height)/2 ###################### HERE
return self.area
If you have
shape = Triangle(1.5,2.5)
shape.area() # returns float assigned at "HERE" to shape.area
shape.area() # try to call that float assigned in previous step at "HERE"

Obtaining values from dictionary in OOP: "AttributeError"

I am trying to get values from my dictionary VALUES. My program creates combination of possible positions and gets the last position. Then I want to get the value. Everything works well here except indicated .get_value method. When I execute this code I receive:
AttributeError: 'Combination' object has no attribute 'get_value'
Theoretically it should be easy but I am new to OOP and I don't see what is wrong here.
X = ['A','B','C']
Y = ['1','2','3']
VALUES = {'A':10, 'B': 50, 'C':-20}
class Combination:
def __init__(self,x,y):
if (x in X) and (y in Y):
self.x = x
self.y = y
else:
print "WRONG!!"
def __repr__ (self):
return self.x+self.y
def get_x(self):
return self.x
def get_y(self):
return self.y
class Position:
def __init__(self):
self.xy = []
for i in X:
for j in Y:
self.xy.append(Combination(i,j))
def choose_last(self):
return self.xy.pop()
def __str__(self):
return "List contains: " + str(self.xy)
class Operation1:
def __init__(self):
self.operation1 = []
def __str__(self):
s = str(self.operation1)
return s
def get_value(self):
V = VALUES.get(self)
return V
pos = Position()
print pos
last_item = pos.choose_last()
print "Last item:", last_item, pos
last_value = last_item.get_value() # <---- Here is a problem
How can I obtain value of my position? Value is determined by the X value - this is A,B or C. In the dictionary I have a numeral value for the letter.
You are appending objects of Combination into xy of Position. When you say choose_last, it will return the last Combination object inserted into xy. And you are trying to invoke get_value method on a Combination object, which doesnt have that method. Thats why you are getting that error.
Always use new style classes.

Why does the print statement at the bottom of my main method not print anything?

I'm working on the MIT open courseware for CS-600 and I can't figure out why the last print statement isn't printing anything. Here's the code I wrote:
#!/usr/bin/env python
# encoding: utf-8
# 6.00 Problem Set 9
#
# Name:
# Collaborators:
# Time:
from string import *
class Shape(object):
def area(self):
raise AttributeException("Subclasses should override this method.")
class Square(Shape):
def __init__(self, h):
"""
h: length of side of the square
"""
self.side = float(h)
def area(self):
"""
Returns area of the square
"""
return self.side**2
def __str__(self):
return 'Square with side ' + str(self.side)
def __eq__(self, other):
"""
Two squares are equal if they have the same dimension.
other: object to check for equality
"""
return type(other) == Square and self.side == other.side
class Circle(Shape):
def __init__(self, radius):
"""
radius: radius of the circle
"""
self.radius = float(radius)
def area(self):
"""
Returns approximate area of the circle
"""
return 3.14159*(self.radius**2)
def __str__(self):
return 'Circle with radius ' + str(self.radius)
def __eq__(self, other):
"""
Two circles are equal if they have the same radius.
other: object to check for equality
"""
return type(other) == Circle and self.radius == other.radius
#
# Problem 1: Create the Triangle class
#
## TO DO: Implement the `Triangle` class, which also extends `Shape`.
class Triangle(Shape):
def __init__(self, base, height):
self.base = float(base)
self.height = float(height)
def area(self):
return self.base*self.height/2
def __str__(self):
return 'Triangle with base ' + str(self.base) + 'and height ' + str(self.height)
def __eq__(self, other):
return type(other) == Triangle and self.base == other.base and self.height == other.height
#
# Problem 2: Create the ShapeSet class
#
## TO DO: Fill in the following code skeleton according to the
## specifications.
class ShapeSet(object):
def __init__(self):
"""
Initialize any needed variables
"""
self.allCircles = []
self.allSquares = []
self.allTriangles = []
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
self.place = None
def addShape(self, sh):
"""
Add shape sh to the set; no two shapes in the set may be
identical
sh: shape to be added
"""
if not isinstance(sh, Shape): raise TypeError('not a shape')
if isinstance(sh, Square):
for sq in self.allSquares:
if sh == sq:
raise ValueError('shape already in the set')
self.allSquares.append(sh)
if isinstance(sh, Triangle):
for tri in self.allTriangles:
if sh == tri:
raise ValueError('shape already in the set')
self.allTriangles.append(sh)
if isinstance(sh, Circle):
for circ in self.allCircles:
if sh == circ:
raise ValueError('shape already in the set')
self.allCircles.append(sh)
def __iter__(self):
"""
Return an iterator that allows you to iterate over the set of
shapes, one shape at a time
"""
self.place = 0
return self
def next(self):
if self.place >= len(self.allShapes):
raise StopIteration
self.place += 1
return self.allShapes[self.place - 1]
def __str__(self):
"""
Return the string representation for a set, which consists of
the string representation of each shape, categorized by type
(circles, then squares, then triangles)
"""
shapeList = ""
for item in self.allShapes:
shapeList += item.get__str__ + "br/"
return shapeList
#
# Problem 3: Find the largest shapes in a ShapeSet
#
def findLargest(shapes):
"""
Returns a tuple containing the elements of ShapeSet with the
largest area.
shapes: ShapeSet
"""
## TO DO
#
# Problem 4: Read shapes from a file into a ShapeSet
#
def readShapesFromFile(filename):
"""
Retrieves shape information from the given file.
Creates and returns a ShapeSet with the shapes found.
filename: string
"""
## TO DO
def main():
sq1 = Square(4.0)
sq2 = Square(5.0)
sq3 = Square(3.0)
circ1 = Circle(3.0)
circ2 = Circle(3.2)
tri1 = Triangle(3.0, 4.0)
tri2 = Triangle(4.0, 3.0)
tri3 = Triangle(1.0, 1.0)
thisSet = ShapeSet()
thisSet.addShape(sq1)
thisSet.addShape(sq2)
thisSet.addShape(sq3)
thisSet.addShape(circ1)
thisSet.addShape(circ2)
thisSet.addShape(tri1)
thisSet.addShape(tri2)
thisSet.addShape(tri3)
print thisSet
if __name__ == '__main__':
main()
This line:
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
doesn't do what you think it does. It sets allShapes to an empty list, and then as you add shapes later, nothing updates allShapes.
Then your __str__ function just loops over allShapes, which is still empty, so your __str__ returns an empty string.
This line makes allShapes an empty list:
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
If you modify allCircles, that doesn't affect allShapes. I would personally eliminate allShapes, and in the str method, add them at the last possible second:
for item in self.allCircles + self.allSquares + self.allTriangles:
The problem is here:
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
When you concatenate lists like this, the result is a copy of the component lists. So when those lists are changed later, the concatenated list isn't changed. In this case, self.allCircles, etc. are all empty. So self.allShapes is an empty list too; the for loop in ShapeSet.__str__ doesn't append anything to ShapeList, and so the result is an empty string.
One simple way to fix this would be to make allShapes a method that you call, and that returns a new concatenation of self.allCircles... etc. each time it's called. That way, allShapes is always up-to-date.
If this is your actual code, then it must be because of
item.get__str__
which should raise an exception.
Edit: as others have noted, this isn't the actual problem, but I leave this here as a hint for further progress. Mind you, it's considered bad style ("unpythonic") to call x.__str__() directly, as you probably intended. Call str(x) instead, even in the implementation of __str__.
You assign allShapes to be the value of self.allCircles + self.allSquares + self.allTriangles at the start in your init method (when the other lists are empty).
It's value is then never changed, so it remains empty.
You need this in addShape:
self.allShapes.append(sh)

Categories