I have to use a class. I have to make sure that x and y are properties.
If the values provided are not convertible to an integer, raise an AttributeError. If we give a value less than 0 to x or y, it is assigned the value 0.
If we give a value greater than 10 to x or y, it is assigned the value 10.
Here is my code:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def getx(self):
x=int()
return self._x
def gety(self):
y=int()
return self._y
if x>=0:
return 0
else if x<=10:
return 10
I want to obtain this:
p = Point(1,12)
print(p.x, p.y) # output "1 10"
p.x = 25
p.y = -5
print(p.x, p.y) # output "10 0"
What are you looking for is clamp() function, which takes 3 arguments: value, desired minimal value and desired maximal value.
Properties are defined by the #property decorator. For testing if the value assigned to property is number I use numbers module. Here is sample code:
import numbers
def clamp(v, _min, _max):
return max(min(v, _max), _min)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
#property
def x(self):
return self.__x
#x.setter
def x(self, value):
if not isinstance(value, numbers.Number):
raise AttributeError()
self.__x = clamp(int(value), 0, 10)
#property
def y(self):
return self.__y
#y.setter
def y(self, value):
if not isinstance(value, numbers.Number):
raise AttributeError()
self.__y = clamp(int(value), 0, 10)
p = Point(1,12)
print(p.x, p.y) # output "1 10"
p.x = 25
p.y = -5
print(p.x, p.y) # output "10 0"
Related
Create a class called Position that manages the position x and y.
Your constructor should take in the initial position of x and of y and upper limits for x and y and then use properties to manage x and y so that they cannot be set above these limits. Note you will need a property (getter/setter) for both x and y.
If an attempt to assign a value above the limit is made then it should raise a ValueError.
class Position:
def __init__(self,x,y,z,value):
self.x=x
self.y=y
pass
#property
def value(self):
return f"{self._x} and {self._y}"
#value.setter
def name(self,value):
self._x = value.upper()
self._y = value.upper()
if value > 10:
raise ValueError("x cannot be bigger than 10")
self._name = value
if __name__ == "__main__":
p = Position(0,0,10,10) # x=0, y=0,
print(f"x={p.x} and y={p.y}") # prints x=0 and y=0
p.x = 2
print(f"x={p.x} and y={p.y}") # prints x=2 and y=0
p.y += 3
print(f"x={p.x} and y={p.y}") # prints x=2 and y=3
p.x = 11 # raises ValueError: x cannot be bigger than 10
If you want to validate each of x and y, you need to created #property and #_.setter for each one of them.
class Position:
def __init__(self, x, y):
self._x = x # private x
self._y = y # private y
#property
def x(self):
return self._x
#x.setter
def x(self, x):
if x > 10:
raise ValueError("x cannot be bigger than 10")
self._x = x
#property
def y(self):
return self._y
#y.setter
def y(self, y):
if y > 10:
raise ValueError("y cannot be bigger than 10")
self._y = y
p1 = Position(11, 12) # note no validation in __init__ func feel free to add it
p1.x = 30 # raises ValueError("x cannot be bigger than 10")
p1.x = 3 # OK
NB. the property method, the _ in _.setter and the setter method must be named the same.
I am learning to use #property decorators. I have the following two code snippets:
Code 1
class P2:
def __init__(self, x):
self.x = x
#property
def x(self):
return self.y
#x.setter
def x(self, x):
if x < 0:
self.y = x
elif x > 1000:
self.y = 1000
else:
self.y = x
p1 = P2(7600)
print(p1.y)
Code2
class P2:
def __init__(self, x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = x
elif x > 1000:
self.__x = 1000
else:
self.__x = x
p1 = P2(7600)
print(p1.__x)
To obtain the code2 I replace y by __x in code1. But the issue is that the code1 is running perfectly fine but the code2 is giving an error 'P2' object has no attribute '__x'.
My understanding is that y and __x are merely two variables, they should behave in the same way, so in my opinion code2 and code1 are identical and both should give same output.
But, it is not happening. What is wrong in my understanding?
Properties that are prepended with a double underscore are pseudo-private. (There is no notion of completely private variables in Python, unlike Java or C++.)
To access pseudo-private member variables, replace the double underscore with _<name of your class>__ (i.e. an underscore, followed by the class name, followed by two underscores). That being said, if you need to do this, you should consider why the variable is pseudo-private in the first place.
class P2:
def __init__(self, x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = x
elif x > 1000:
self.__x = 1000
else:
self.__x = x
p1 = P2(7600)
print(p1._P2__x) # Prints 1000
class A(object):
def __init__(self, x):
self.x = x
def f(self, x):
return 2*x
def g(self, x):
return self.f(x)
class B(A):
def g(self, y):
return 3*y + self.x
class C1(B):
def __init__(self, x, y):
B.__init__(self,x)
self.y = y
def f(self, x):
return self.x + self.y
class C2(B):
def __init__(self, x, y):
B.__init__(self,x)
self.y = y
def f(self, x):
return x + self.x + self.y
a = A(5)
b = B(2)
c1 = C1(3,5)
c2 = C2(3,5)
When I do "What does the expression c2.f(4) evaluate to?", I was not sure where self.x in the f function in class C2 points to.
Could you give me some suggestions?
c2.f(4) makes 12. The value of x in function f is 4 because that is the argument in c2.f(4). The value of c2's self.x is 3 because C2 inherits from B, which inherits from A, where the line self.x = x occurs. In this line, x is what is entered in the line c2 = C2(3,5) and because it is assigned to self.x, c2's self.x is 3. Because of the line self.y = y in class C2, the instance c2's y value is what is entered in the line c2 = C2(3,5), 5. 4 + 3 + 5 makes 12.
In the following code snippet, I use a setter on the x attribute (which I'd like to keep private)
class test:
def __init__(self, pos, x):
self._pos = pos
self.x = x # Want to be a private variable, eg. self._x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
# Logic for setting x
if self._pos == 'long':
self._x = -1 * abs(x)
elif self._pos == 'short':
self._x = abs(x)
else:
raise ValueError('$$ pos must be long or short')
The problem is I end up with TWO attributes , self.x AND self._x . Since I want x to be private - I'd like to only have self._x (and discard self.x) . What's missing in the code ?
I'm having trouble on how to implement property to protect attributes.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
if '_x' in dir(self):
raise NotImplementedError("Cannot change x coordinate")
else:
self._x = x
def get_x(self):
return self._x
#I beleive my mistake is here. I'm not sure if I'm implementing this correctly
x = property(get_x, set_x, None, None)
So I want to prevent any user from changing the x-coordinate. My question is, how do I get python to redirect the user to the set_x() and get_x() methods? I've tried running this code in terminal and whenever I apply the following, the point changes.
p = point(3, 4)
p.x = 5 #x is now 5
You only need this much:
class Point:
def __init__(self, x, y):
self._x = x
self.y = y
def get_x(self):
return self._x
x = property(get_x)
You can set the hidden field self._x in your init, then you don't need a setter for x at all. And have get_x return self._x rather than self.x so it doesn't try and call itself.
You can use the #property decorator to do this even more succinctly.
class Point:
def __init__(self, x, y):
self._x = x
self.y = y
#property
def x(self):
return self._x
The following code works on both python2.x and python3.x:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
if '_x' in dir(self):
raise NotImplementedError("Cannot change x coordinate")
else:
self._x = x
def get_x(self):
return self._x
x = property(get_x, set_x, None, None)
p = Point(2, 3)
print(p.x) # 2
p.x = 6 # NotImplementedError
Pretty much all I did was inherit from object (to get it to work on python2.x) and use the name Point rather than point (which would have been a NameError before).
There are other things you can do to clean it up a bit (e.g. khelwood's suggestion of just writing the getter -- or DSM's suggestion of using hasattr instead of '_x' in dir(self)).
Note, if you really just want a type that takes an x and y arguments that you want to be immutable -- Maybe you should consider using a colledctions.namedtuple
from collections import namedtuple
Point = namedtuple('Point', 'x,y')
p = Point(2, 3)
p.x # 2
p.y # 3
p.x = 6 # AttributeError: can't set attribute