Just learning python and I write simple code along the way.
Overriding the print method in the derived class, can I print both classes in the same line, without changing the base class?
i.e. I want pointA.print() to print
"x = 10 and y = 10 and z = 10"; not x = 10 and y = 10 and z = 10
I know in C# this can be done easily as the WriteLine method starts with a new line (as opposed to ending with it), and so, the Write method does what I expected it to.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def print(self):
print(f"x = {self.x} and y = {self.y}")
class PointWithZAxis(Point):
def __init__(self, x, y, z):
Point.__init__(self, x, y)
self.z = z
def print(self):
Point.print(self)
print(f"and z = {self.z}")
pointA = PointWithZAxis(10, 10, 10)
pointA.print()
Change the print in the parent class to this:
print(f"x = {self.x} and y = {self.y}", end = "")
BTW, it is cleaner in my opinion to overwrite the __str__ method of the classes than to make a print() method. The __str__ method of an object is automatically called when you print the object (like: print(object)). The code then becomes:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"x = {self.x} and y = {self.y}"
class PointWithZAxis(Point):
def __init__(self, x, y, z):
Point.__init__(self, x, y)
self.z = z
def __str__(self):
return super().__str__() + f"and z = {self.z}"
pointA = PointWithZAxis(10, 10, 10)
print(pointA)
You can add end="" to print
print("something", end="")
Adding the end argument to the print function lets you specify the ending of you print statement, so:
print(..., end='')
By default it is set to end='\n', which indicates a new line.
P.S. be careful with naming funcions print as they shadow already existing functions and may cause confusion.
There are a couple other people who are responding with print(..., end=""). This is a quick-fix, sure. But there's something else I'd like to point out.
Since you're a beginner, I'd say you've encountered a good example that teaches why you should think a little about the kind of APIs/interfaces you're designing. Take a step back and ask yourself this question: "Who are the consumers (callers) of def print(self)? And what do they want." The point here is, you can have different callers each wanting a different thing. These problems arise when you're not able to find the common denominator across all that they want. Introducing parameters to the function is one way. In other words: your derived class can send in a additional parameter asking to not append a newline, while those that call from outside of PointWithZAxis.print(..) need not know about this parameter.
Here's an average solution:
class Point:
....
def print(self, include_newline=True):
print(..., end=("\n" if include_newline else ""))
class PointWithZAxis:
def print(self):
Point.print(self, include_newline=False)
print(....)
Here's a good solution (Don't print. How about making the job just to construct a string? Actual printing can be the callers job).
class Point:
....
def get_print_string(self):
return f"x = {self.x} and y = {self.y}"
class PointWithZAxis:
def get_print_string(self):
return super().get_print_string() + f" and z = {self.z}"
# You can then do:
p = PointWithZAxis()
print(p.get_print_string())
Pythonic solution (recommended): Use __str__(..). This is the recommended way of doing what get_print_string() does.This function indirectly gets called when you do print(AnyClass()). Refer to #Bram Dekker's solution.
Related
I have a simple class that adds 2 nos. Before adding 2 nos I pass a helper function that appends 2 zeros and passes the result.
When I try to print the add_nos.provide(append_zeros) it always shows None.
def append_zeros(x,y):
x = int(str(x) + '00' )
y = int(str(y) + '00')
print x+y
return x + y
class Add_Nos():
def __init__(self,input_array):
self.input_array = input_array
def provide(self,callback):
for each in self.input_array:
x,y = each
callback(x,y)
add_nos = Add_Nos([(1,2),(3,4)])
print add_nos.provide(append_zeros)
The method add_nos.provide(self, callback) has no return statement, thus it returns nothing, which in python means that it returns None.
To avoid this, either add a return statement to provide() or simply call the method without print.
It's not entirely clear what you are trying to do, but provide does not return anything. In python, the default return type of any function is None, so implicitly printing add_nos.provide(append_zeros) will do the function call, and then return None behind the scenes.
One option you have is to return self.input_array:
class Add_Nos():
def __init__(self,input_array):
self.input_array = input_array
def provide(self,callback):
for each in self.input_array:
x,y = each
callback(x,y)
return self.input_array
Note that you can also do for x, y in self.input_array: :)
Presumably, you actually want to be getting a new list out with the result of the computation. In this case, this is an excellent candidate for a list comprehension:
def provide(self,callback):
return [callback(x, y) for x, y in self.input_array]
This is a one-line equivalent of doing
def provide(self, callback):
ret = []
for x, y in self.input_array:
ret.append(callback(x, y))
return ret
You said:
I want the result to be 300 in the first instance and 700 in the
next instance, kind of generate a iterator object.
So you simply need to turn the .provide method into a generator, and then call it appropriately. Like this:
def append_zeros(x,y):
x = int(str(x) + '00')
y = int(str(y) + '00')
#print x+y
return x + y
class Add_Nos():
def __init__(self,input_array):
self.input_array = input_array
def provide(self,callback):
for each in self.input_array:
x,y = each
yield callback(x,y)
add_nos = Add_Nos([(1,2),(3,4)])
for t in add_nos.provide(append_zeros):
print t
output
300
700
That append_zeros function is a bit strange. Rather than converting the args to strings so you can append the zeros and then converting the results back to ints to do the arithmetic yu should simply multiply each arg by one hundred.
Also, you can make the .provide method a little more streamlined by using "splat" unpacking. And as tyteen4a03 mentioned, in Python 2 your Add_Nos class ought to inherit from object so that you get a new-style class instead of the deprecated old-style class. So here's another version with those changes; it produces the same output as the above code.
def append_zeros(x, y):
return x * 100 + y * 100
class Add_Nos(object):
def __init__(self, input_array):
self.input_array = input_array
def provide(self, callback):
for each in self.input_array:
yield callback(*each)
add_nos = Add_Nos([(1,2),(3,4)])
for t in add_nos.provide(append_zeros):
print t
To get to the point I'm learning how to work classes and list comprehension and I'm running into this problem:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def are_in_first_quadrant(listPoint):
newListPoint = filter(lambda pnt: pnt.x > 0 and pnt.y > 0, listPoint)
return newListPoint
pList = [Point(-3,7), Point(2,3), Point(7,0), Point(6,-9), Point(7,9)]
newList = are_in_first_quadrant(pList)
So you can see the goal of this is to spew out a list of points that are in the first quadrant, but when I try to print 'newList' I get:
[<objects.Point instance at 0x0293FA08>, <objects.Point instance at 0x0293FA80>]
Instead of:
[Point(2,3) , Point(7.9)]
Looking over this post: Filters in Python3
I understand the print out is the memory location but I don't really gain much more from that.
So question is how exactly do I fix this?
I'm guessing it probably has to do with how I used lambda but again not too sure.
I'm using Python 2.7
Thanks in advance.
Edit:
Also just tried
def are_in_first_quadrant(listPoint):
newListPoint = [pnt for pnt in listPoint if pnt.x > 0 and pnt.y > 0]
return newListPoint
and it throws up the same thing.
You need to provide a __repr__() method for your Point class:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return "Point({}, {})".format(self.x, self.y)
This method will be called for each element when the list is printed.
Just a remark: a function called are_... (or is_...) is supposed to return True or False. A more suitable name would be points_in_first_quadrant().
For a comparison between __str__() and __repr__(), see this question.
I was wondering, is there a way in which I can cast a class instance to a tuple in the following way:
Say we have a class A with fields x and y, and we have a list l which contains A instances. I would like to do the following if it's possible:
for (x, y) in l:
print x + y #just an example
I was thinking to use __iter__ to return x and y but it didn't quite work. casting to tuple was working fine but it couldn't autobox the objects in l to (x,y) in the for loop
I know it sounds little silly but a friend was challenging me for something he was working on and I just couldn't do it even though it seems doable
Well, you can do it with __iter__:
def __iter__(self):
yield self.x
yield self.y
but I wouldn't recommend it.
If you're sure you want this thing to be treatable as a tuple, you could use a namedtuple class:
import collections
XAndYClass = collections.namedtuple('XAndYClass', 'x y')
xandy = XAndYClass(1, 2)
print xandy.x # prints 1
x, y = xandy # x = 1, y = 2
You can inherit from that to add methods, if you want.
class XAndYWithMethods(XAndYClass):
def method(self):
print 'doing stuff...'
EDIT: I assure you that everything is correctly indented when I run this through python. I assume the problem has to do with how I call p._x and p._y but I'm not sure why?
This is a problem I am having crazy amounts of difficulty with. It just doesn't make sense. I have to submit this through a program checker, so it checks the code and makes sure its correct. If its not correct it gives out a useless error report.
Ok so this is what I want to do:
I need to calculate the distance between two vectors(x,y).
This is the test case that the program will run:
foo = Point(1,2)
bar = Point(3,4)
foo.dist_to_point(bar) = 2.8...
This is the code I have:
import math
class Point:
def __init__(self, x, y):
self._x = x
self._y = y
def dist_to_point(self, p):
a = self._x - p._x
b = self._y - p._y
c = math.sqrt(a**2+b**2)
return c
and it doesn't work. The error it gives me is this "you are trying to index into a point object. They are not lists or tuples - for point object p use p._x and p._y to access the required variables.
Any ideas?
Python requires all of your indentation to be the same. I see you're using a mixture of tabs and spaces, so your code won't even compile correctly. Here is the same code you have above, just with all of the indentation being two spaces instead of a mixture of tabs and spaces (it compiles and works!):
import math
class Point:
def __init__(self, x, y):
self._x = x
self._y = y
def dist_to_point(self, p):
a = self._x - p._x
b = self._y - p._y
c = math.sqrt(a**2+b**2)
return c
foo = Point(1,2)
bar = Point(3,4)
print(foo.dist_to_point(bar))
Pick tabs or spaces - don't use both. :)
Wild guesses:
Elsewhere, in code not shown, you are supposed to use a collection of points, but you use a single point.
You are supposed to implement indexed access to point coordinates.
You should review the assignment.
Let's say I've got a variable A that is the result of a function/expression F. F in it's turn has a number of other variables in it, let's say X,Y and Z.
Is it possible to bind A to F so that whenever X,Y or Z changes, A will be updated automatically?
What I want to avoid is that everytime X,Y and Z changes, I have to remember to update A explicitly in the code. I also don't want to call the function everytime I want to use the A.
Example (as per requested): I've got the following function:
def calcHits():
return sum(hitDiceRolls,level*modList['con'])
and in my program (outside of the function), I've got a variable called hitPoints (yes, it's a roleplaying game program). Whenever the variables that's used in the function is changed, I want hitPoints to change as well.
The typical way to do this in Python would be to use a class:
class ExpressionBinder:
def __init__(self, f):
self.f = f
self.x = 0
self.y = 0
self.z = 0
#property
def result(self):
return self.f(self.x, self.y, self.z)
You can use it like this:
def f(x, y, z):
return x**3 + y**2 + z
b = ExpressionBinder(f)
b.x = 1
b.y = 2
b.z = 3
print(b.result)
There is no way in Python to automatically rebind a name in global or local scope in response to other names being rebound. However, it should be possible to make a class that can keep track of some values and have a member function that returns the value you called A. And, as #Alok pointed out, you can use property descriptors to make a member name that implicitly calls a function to return its value, so you can hide the function and treat the name like a plain old name.
class Trk(object):
"""Track some values and compute a function if any change"""
def __init__(self, name, fn, **objects_to_track):
def _trk_fn(self):
if any(self.__dict__[x] != self.original_objects[x] for x in self.original_objects):
self.value = self.saved_fn(self.__dict___)
# now that self.value is updated, also update self.original_objects
for x in self.original_objects:
self.original_objects[x] = self.__dict__[x]
return self.value
self.original_objects = objects_to_track # make reference copy
self.__dict__.update(objects_to_track)
self.name = name
self.saved_fn = fn
self.fn = self._trk_fn()
self.value = self.fn()
I'm sorry but I am very tired right now, and I canot finish this example. I didn't test it either. But this shows one way to track values, and if they are different, do something different. You use it like this:
# want to track x, y, z
trk = Trk(x, y, z)
trk.fn() # returns up-to-date value
trk.x = new_value
trk.fn() #detects that trk.x changed and computes new trk.value
If the above works, you can use the property descriptor stuff to bind a name such that an attempt to read a value from the name will call self.fn()
EDIT: Oh, it's important that when self.value is updated, self.original_objects should be updated. I've added code to do that.
And now I'm going to sleep!