Python - error message when not using private member [duplicate] - python

This question already has an answer here:
Setting property causes maximum recursion depth exceeded
(1 answer)
Closed 5 months ago.
I get an error when i run the following:
class Product:
def __init__(self, price):
self.setprice(price)
def getprice(self):
return self.price
def setprice(self, value):
if value < 0:
raise ValueError("Price cannot be negative")
self.price = value
price = property(getprice, setprice)
x = Product(23)
print(x.price)
The error is below:
File "c:\Users\Sonam\OneDrive\Desktop\PYTHON\tempCodeRunnerFile.py", line 9, in setprice self.price = value [Previous line repeated 993 more times] File "c:\Users\Sonam\OneDrive\Desktop\PYTHON\tempCodeRunnerFile.py", line 7, in setprice if value < 0: RecursionError: maximum recursion depth exceeded in comparison
However when i make the price a private member, it runs ok :
class Product:
def __init__(self, price):
self.setprice(price)
def getprice(self):
return self.__price
def setprice(self, value):
if value < 0:
raise ValueError("Price cannot be negative")
self.__price = value
price = property(getprice, setprice)
x = Product(23)
print(x.price)
Can anyone tell me why we get this error???? Why do we need to make price private in the setprice method?

The appropriate way to define properties with property is:
class Product
def __init__(self, price):
self.price = price
#property
def price(self):
return self._price
#price.setter
def price(self, value):
if value < 0:
raise ValueError("Price can't be negative.")
self._price = value
Then you can use it as you would expect:
>>> p = Product(3)
>>> p.price
3
>>> p.price = -1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in price
ValueError: Price can't be negative.
>>> p.price
3
>>> p.price = 4
>>> p.price
4
>>>
Your error stems from the fact that when you define self.price = ..., you actually call the setter once again, thus recurring over and over again, until the max depth recursion limit is reached. When you set self._price instead, you are not using the setter, but doing a regular assignment instead.

Related

numpy.ndarray' object has no attribute 'append' - error comes up when creating stack using numpy

I want to create a stack using numpy
I tried following code but "numpy.ndarray' object has no attribute 'append' " this error comes
I tried replace the append with other attributes , but any of those methods didnt work for me .
can someone run the codes in your pc and suggest me a way to fix this.
Traceback (most recent call last):
File "C:\Users\Banchi\Desktop\numpy\main.py", line 3, in <module>
myStack.push(11)
File "C:\Users\Banchi\Desktop\numpy\stackf.py", line 8, in push
return self.Stack.append(data)
AttributeError: 'numpy.ndarray' object has no attribute 'append'
stackf.py
import numpy as np
class Stack:
def __init__(self):
self.Stack =np.array([])
def push(self,data):
return self.Stack.append(data)
print("stack overfolow")
def pop(self):
if len(self.Stack)==0:
print("stack underflow")
else:
return self.Stack.pop()
def __len(self):
return len(self.Stack)
def top(self):
if len(self.Stack)==0:
print("stack under flow")
else:
return self.Stack[len(self.Stack)-1]
def is_empty(self):
return len(self.Stack) == 0
def is_full(self):
return len(self.Stack) == self.limit
def size(self):
return len(self.Stack)
main.py
from stackf import Stack
myStack = Stack()
myStack.push(11)
myStack.push(11)
myStack.push(11)
myStack.push(11)
print(myStack.size())
myStack.push(10)
myStack.push(20)
myStack.push(30)
print(myStack)
You can define your class e.g. as:
class Stack:
def __init__(self, limit : int = 0):
self.arr = np.array([])
self.limit = limit
def is_empty(self) -> bool:
return self.size() == 0
def pop(self):
n = self.size() - 1
if n < 0:
raise Exception('pop - Stack is empty')
rv = self.arr[n]
self.arr = np.delete(self.arr, n)
return rv
def push(self, data):
if self.limit > 0 and self.limit == self.size():
raise Exception('push - Limit exceeded')
self.arr = np.append(self.arr, data)
def size(self) -> int:
return self.arr.size
def top(self):
if self.size() == 0:
raise Exception('top - stack underflow')
return self.arr[-1]
def __str__(self):
return np.array_str(self.arr)
Note that I added some functionality concerning the limit, but the
default value of limit is 0, meaning "no limit".
Since the class contains size() method, there is no need to repeat
just the same code in __len() method.
I added also some exceptions instead of printing an error message.
__str__ method is added to get any meaningful result when you print
a Stack instance.
Then, if you run your main code, the result is:
4
[11. 11. 11. 11. 10. 20. 30.]
But if you create your Stack instance e.g. as myStack = Stack(3)
(with the limit set to a non-zero value), then at some point you
will get Exception: push - Limit exceeded.

How do i get rid of attribute error for my class in Python?

This error shows up when I run my code. AttributeError: 'int' object has no attribute 'vertices'. I think I might have passed the parameter incorrectly for vertices. I'm trying to store the points in a list but do I not need to unpack with *?
Here is the beginning of my class:
class SimplePoly:
def __init__(self, *vertices):
self.vertices = list(vertices) ##attribute error
##tring to use a list of point instead of a list with one tuple
self.count = 0
self.length = len(self.vertices)
def translate(self, s, t):
for i in self.vertices:
i.translate(s,t)
def rotate(self, degree):
degree = math.radians(degree)
for i in self.vertices:
i.rotate(degree)
def __iter__(self):
return self
def __next__(self):
"""return next vertex from polygon, raises exception if no more"""
if self.count > self.length - 1:
raise StopIteration
else:
answer = self.vertices[self.count]
self.count += 1
return answer
def __len__(self):
"""returns number of vertices in the polygon"""
count = 0
for i in self.vertices:
count += 1
return count
def __getitem__(self, index):
"""overloads the index operator"""
if index < 0 or index > len(self.vertices):
raise IndexError
return self.vertices[index - 1]
def __str__(self):
points = []
for i in self.vertices:
points.append(str(i))
return ','.join(points)
The error obtained is as follows:
Traceback (most recent call last):
File "/Users/halled/Downloads/testpolys-3.py", line 111, in <module>
octagon = RegularPoly(8)
File "/Users/halled/Documents/polys.py", line 186, in __init__
ConvPoly.__init__(vertices)
File "/Users/halled/Documents/polys.py", line 178, in __init__
SimplePoly.__init__(self, *vertices)
File "/Users/halled/Documents/polys.py", line 62, in __init__
self.vertices = list(vertices)
AttributeError: 'int' object has no attribute 'vertices'
Looking at the traceback, Line 186 ("/Users/halled/Documents/polys.py") seems to be the problem.
ConvPoly.__init__(vertices)
You probably want this instead of the above line -
ConvPoly.__init__(self, vertices)
From traceback, vertices is set to 8.
ConvPoly.__init__(vertices) is called with vertices=8.
This in turn calls SimplePoly.__init__(self, *vertices)
Here, self gets vertices which is int.
As a result, exception is raised when self.vertices = list(vertices) is executed since self is pointing to 8 (int).

Unit testing method that raises and exception

I try to unit-test if method responsible for returning price of given product raises an exception if we pass bad item_id - find_price_of_given_id.
Test:
import unittest
from Automat import Automat
from Bank import Bank
from Item import Item
from exceptions.NoItemException import NoItemException
class AutomatTest(unittest.TestCase):
def test_checkPriceOfGivenID(self):
bank = Bank()
automat = Automat(bank)
Cola = Item(2)
automat.add_object(Cola)
self.assertEqual(automat.find_price_of_given_id(30), 2)
def test_checkPriceOfGivenIDWithInvalidID(self):
bank = Bank()
automat = Automat(bank)
Cola = Item(2)
automat.add_object(Cola)
self.assertRaises(NoItemException, automat.find_price_of_given_id(31))
Automat class:
if __name__ == '__main__':
unittest.main()
from Item import Item
from exceptions.NoItemException import NoItemException
from exceptions.NoProperAmountException import NoProperAmountException
from Coin import Coin
from decimal import *
class Automat:
def __init__(self, _bank, objects=None):
self.item_id = 30
self.bank = _bank
if objects is None:
objects = {}
self.objects = objects
self.purchaseItemBank = []
def add_object(self, obj: Item):
id_to_assign = self.item_id
self.objects.update({id_to_assign: obj})
self.item_id += 1
return id_to_assign
def find_price_of_given_id(self, item_id):
if self.objects.get(item_id) is not None:
return self.objects.get(item_id).get_price()
else:
raise NoItemException
def find_amount_of_given_id(self, item_id):
if self.objects.get(item_id) is not None:
return self.objects.get(item_id).get_amount()
else:
raise NoItemException
def checkIfAmountIsPositive(self, item_id):
if self.objects.get(item_id) is not None:
var = True if self.objects.get(item_id).get_amount() > 0 else False
return var
else:
raise NoItemException
def withdrawItem(self, item_id):
self.objects.get(item_id).decrease()
def getAmountOfGivenNominal(self, coinValue):
counter = 0
for iterator in self.bank.bank:
if iterator.getCoinValue() == coinValue:
counter += 1
return counter
Unfortunately I got
Error
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\lib\unittest\case.py", line 59, in testPartExecutor
yield
File "C:\ProgramData\Anaconda3\lib\unittest\case.py", line 615, in run
testMethod()
File "C:\Users\Admin\PycharmProjects\vending-machine\AutomatTest.py", line 22, in test_checkPriceOfGivenIDWithInvalidID
self.assertRaises(NoItemException, automat.find_price_of_given_id(31))
File "C:\Users\Admin\PycharmProjects\vending-machine\Automat.py", line 28, in find_price_of_given_id
raise NoItemException
exceptions.NoItemException.NoItemException
Ran 1 test in 0.003s
FAILED (errors=1)
Process finished with exit code 1
Item class:
class Item:
def __init__(self, price, amount=5):
self.amount = amount
self.price = price
def get_price(self):
return self.price
def get_amount(self):
return self.amount
def decrease(self):
self.amount -= 1
def __str__(self):
return f"{self.amount} # {self.price}"
self.assertRaises(NoItemException, automat.find_price_of_given_id(31))
You're calling the method, and then pass its return value into assertRaises; but before that can happen, the exception is already raised. This is not how you use assertRaises. You can use it in two ways:
Pass the method you want to call and its arguments to assertRaises:
self.assertRaises(NoItemException, automat.find_price_of_given_id, 31)
Note: no (), you're not calling it yourself!
Use it as context manager:
with self.assertRaises(NoItemException):
automat.find_price_of_given_id(31)

How to prevent my code from creating a 'nonetype' variable

These are the codes related to my problem: (Edited for the full codes, I apologize for any confusion caused)
class House:
def __init__(self, location, price, housetype, sqft):
self.location = str(location)
self.price = float(price)
self.housetype = str(housetype)
self.sqft = int(sqft)
if self.price < 0 or self.sqft < 100:
print("Input a valid value.")
def getLocation(self):
return self.location
def getPrice(self):
return self.price
def getHousetype(self):
return self.housetype
def getSqft(self):
return self.sqft
def setLocation(self, newLocation):
self.location = newLocation
def setPrice(self, newPrice):
if newPrice > 0:
self.price = newPrice
else:
print("Input a valid value.")
def setHousetype(self, newHousetype):
self.housetype = newHousetype
def setSqft(self, newSqft):
if newSqft > 100:
self.sqft = newSqft
else:
print("Input a valid value.")
def tax(self, no_years):
if self.getHousetype == 'Apartment':
return self.price * (0.025 * no_years)
elif self.getHousetype == 'Bungalow':
return self.price * (0.035 * no_years)
elif self.getHousetype == 'Condominium':
return self.price * (0.045 * no_years)
else:
print("Invalid data.")
def __eq__ (self, other):
if self.getHousetype() == other.getHousetype():
return 'true'
else:
return 'false'
def __lt__ (self, no_years, other):
if self.tax(no_years) < other.tax(no_years):
return 'true'
I created two house variables according to my format, but when I run house1.__lt__(5, house2), it gives me the error:
'<' not supported between instances of 'NoneType' and 'NoneType'.
I have been trying various ways to solve this problem for about an hour, but I'm really new to python and don't know how I can prevent my code from getting these 'NoneType' values, nor can I change them to an integer for my code for the method __lt__. Could someone tell me how I can solve this?
self.getHousetype on its own is a method object, so self.getHousetype == 'Apartment' is always false. You need to call the method, i.e. self.getHousetype() == 'Apartment'. Since all your conditions are false at the moment, none of your return statement happens and the function ends by returning the default None.
But as cricket_007 pointed out, python has no need for getter methods, so just do self.housetype == 'Apartment'
self.housetype

Python Error: List Object Not Callable with For Loop

Alright so i'm having this TypeError: 'list' object is not callable
It's on the for loop below the (if type=='D')
Exact error is as follows:
Traceback(most recent call last):
file"test.py", line 55 in <module>
main()
File "test.py", line 49, in main
for i in range(len(accountlist())):
TypeError: 'list' object is not callable
My code is below, i've tried putting each parenthesis in brackets and renaming the list to something different, always getting around the same error.
What am i doing wrong here?
class BankAccount:
def __init__(self, getbankaccount, inputAmount=0):
self.__balance = inputAmount
self.__numDeposits = 0
self.__numWithdrawals = 0
self.__totalDeposits = 0
self.__totalWithdrawals = 0
self.__getbankaccount=getbankaccount
def getBalance(self):
return self.__balance
def getNumDeposits(self):
return self.__numDeposits
def getNumWithdrawals(self):
return self.__numWithdrawals
def getTotalDeposits(self):
return self.__totalDeposits
def getTotalWithdrawals(self):
return self.__totalWithdrawals
def getbankaccount(self):
return self.__getbankaccount
def Deposit(self,amount):
self.__balance = self.__balance + amount
self.__numDeposits = self.__numDeposits + 1
self.__totalDeposits = self.__totalDeposits + amount
return self.__balance
def Withdrawal(self,amount):
if (self.__balance >= amount):
self.__balance = self.__balance - amount
self.__numWithdrawals = self.__numWithdrawals + 1
self.__totalWithdrawals = self.__totalWithdrawals + amount
return True
else:
return False
def main():
accountlist=[]
numbers=eval(input())
for i in range(numbers):
account=input()
amount=eval(input())
initial=BankAccount(account, amount)
accountlist.append(initial)
type=input()
while type!='#':
if type=='D':
account=input()
amount=eval(input())
for i in range(len(accountlist())):
if(account==accountlist[i].getbankaccount()):
index=i
accountlist[index].Deposit(amount)
Print(amount, type, account)
type=input()
main()
Your problem is that in the line for i in range(len(accountlist())): you have accountlist(). accountlist is a list, and the () means you're trying to call it like you would a function. Change the line to for i in range(len(accountlist)): and you should be all set.
On a sidenote, it's easy to recognize your problem from your error:
TypeError: 'list' object is not callable
is telling you exactly what you need to know: that you're trying to "call" a list on line 49. Learning to read error messages is an important and useful skill.

Categories