_init__() takes 1 positional argument but 2 were given - python

I'm testing some code for a course OOP, but I run into a problem. I am programming a circle and a cylinder, with the circle class also in the init of the cylinder. I have 2 arguments for the cylinder, but when I give 2 arguments, it's said that I only need 1 and if I give one argument than it gaves the output one is missing.
with the variable a it works, but the error is in variable b. What do I wrong
import math
class CCircle:
def __init__(self):
self._radius = 0
#property
def area(self):
return self._radius**2 * math.pi
#area.setter
def area(self, value):
self._radius = math.sqrt(value / math.pi)
#property
def circumference(self):
return self._radius * 2 * math.pi
#circumference.setter
def circumference(self, value):
self._radius = value / (2 * math.pi)
class CCylinder:
def __init__(self, radius, height):
self._circle = CCircle(radius)
self._height = height
#property
def circumference(self):
return self._circle.circumference
#property
def ground_area(self):
return self._circle.area
#property
def total_area(self):
return self._circle.area + self._height * self._circle.circumference
#property
def volume(self):
return self._circle.area * self._height
a = CCircle()
b = CCylinder(1,4)
init() takes 1 positional argument but 2 were given

You should have your CCircle class start like this
class CCircle:
def __init__(self, radius=0):
self._radius = radius
so that you get the default radius of 0 that you seem to want, but can also initialize it with a radius value like you're doing in the init code of your CCylinder class.

The problem is with this line:
self._circle = CCircle(radius)
but __init__ for the CCircle class does not take any arguments (except for self) so this is causing the error.

You might have had a package folder locally at the place where the .py file is , delete that and that should solve your issue

Related

python decorator with lazy property

def lazyproperty(func):
name = '_lazy_' + func.__name__
#property
def lazy(self):
print(self)
if hasattr(self, name):
return getattr(self, name)
else:
value = func(self)
setattr(self, name, value)
return value
return lazy
import math
class Circle:
def __init__(self, radius):
self.radius = radius
#lazyproperty
def area(self):
print('Computing area')
return math.pi * self.radius ** 2
I am new to python property and decorators. When reading some examples like above,
I have difficulty understanding how it works.
For example, I do not quite get how the "self" inside the lazy definition is the Circle object.Could any one elaborate this example? Thanks!
There is nothing special about the name self; it's just the conventional name given to the first parameter of a function intended to be used as a method. In this case, that function is defined inside lazyproperty instead of directly in the class Circle.
It might help to see the same code written without decorator syntax.
def lazyproperty(func):
name = '_lazy_' + func.__name__
# The "method" to be turned into a property
def lazy(self):
print(self)
if hasattr(self, name):
return getattr(self, name)
else:
value = func(self)
setattr(self, name, value)
return value
# Return the property
return property(lazy)
import math
class Circle:
def __init__(self, radius):
self.radius = radius
# The method to be wrapped by lazyproperty
def area(self):
print('Computing area')
return math.pi * self.radius ** 2
# The actual wrapping to make area a (lazy) property
area = lazyproperty(area)

python class: why do I get maximum recursion when trying to set an attribue with #property decorator?

I am learning how to create classes in python where when modifying attribute 1 will change attribute 2.
For instance a circle with an attribue radius.
class Circle():
def __init__(self,radius):
self._radius = radius
#property
def radius(self):
return self._radius
#radius.setter
def radius(self, value):
self.radius = value
#property
def area(self):
return 3.14*self.radius*self.radius
And this happens:
c = Circle(3)
c.area
All fine. but now:
c.radius = 56
print(c.area)
gives:
RecursionError: maximum recursion depth exceeded
The question is why?
What is the way to force recalculation of other attributes when one is changed?
I have consulted several answers:
Python: modifying property value
How do you change the value of one attribute by changing the value of another? (dependent attributes)
EDIT:
According to some answers (see bellow) I am in an infinite loop. So I delete the part of the setter. I remain with:
class Circle():
def __init__(self,radius):
self._radius = radius
#property
def radius(self):
return self._radius
#property
def area(self):
return 3.14*self.radius*self.radius
What happens then is:
c = Circle(3)
c.radius = 30
error: AttributeError: can't set attribute
After the comments posted here this is the answer with exploratory explanation:
class Circle():
def __init__(self, _radius, color):
print('init run')
self._radius = _radius
self.color = color
#property
def radius(self):
print('property radius run')
return self._radius
#radius.setter
def radius(self, value):
print('setter radius run')
self._radius = value
#property
def area(self):
print('property AREA run')
return 3.14*self.radius*self.radius
I added print statements because if you want to find out what really happens I recommend you to run this code in separate cells in a notebook:
c= Circle(3,'azul') # here only __init__ runs
print(c.radius) # here property radius run
print(c.area)
This is the interesting part. When printing c.area things happen. Before running the code think about what do you think it would happenI was surprised but relieved because I understood now the functioning.
This is what happens: property AREA run, property radius run, property radius run.
My lessons learnt: Accessing the radius property is done via the method. This WAS NOT AT ALL OBVIOUS FOR ME.
Now try to change the radius
c.radius = 56 # setter method runs
print(c.area) # the same ass before.

Using **kwargs on super() gives me Attribute Error

Just trying to create a simple toy example to calculate the surface area of a pyramid:
class Rectangle:
def __init__(self, length, width, **kwargs):
self.length = length
self.width = width
#super().__init__(**kwargs)
def area(self):
return self.length * self.width
def perim(self):
return 2 * (self.length + self.width)
class Square(Rectangle):
def __init__(self, length, **kwargs):
super().__init__(length = length, width = length, **kwargs)
class Triangle:
def __init__(self, base, height, **kwargs):
self.base = base
self.height = height
super().__init__(**kwargs)
def tri_area(self):
return 0.5 * self.base * self.height
class Pyramid(Square, Triangle):
def __init__(self, base, slant, **kwargs):
kwargs["height"] = slant
kwargs["length"] = base
super().__init__(base = base, **kwargs)
def surf_area(self):
return super().area() + 4 * super().tri_area()
p = Pyramid(2,4)
p.surf_area()
But this gives me an error of
AttributeError Traceback (most recent call last)
<ipython-input-154-d97bd6ab2312> in <module>
1 p = Pyramid(2,4)
----> 2 p.surf_area()
<ipython-input-153-457095747484> in surf_area(self)
6
7 def surf_area(self):
----> 8 return super().area() + 4 * super().tri_area()
<ipython-input-151-8b1d4ef9dca9> in tri_area(self)
5 super().__init__(**kwargs)
6 def tri_area(self):
----> 7 return 0.5 * self.base * self.height
AttributeError: 'Pyramid' object has no attribute 'base'
The online resources don't seem to give much of a conceptual understanding of **kwargs too well (or they're written in too much a labrintyine manner for a beginner). Does this somehow have to do with the fact that **kwargs as an iterable need to be exhausted completely before going to the next call?
I can sort of understand why you'd be confused. The main trick to realise is this: absolute bottom line, kwargs is not something magical. It is just a dictionary holding key value pairs. If a call requires more positional arguments than provided, it can look into keyword arguments and accept some values. However, kwargs does not invoke some magic that associates all names provided as a self.<some_name_here> .
So, first to just get a visual understanding of what's going on, what you should do when you don't understand a piece of code is making sure it runs how you think it does. Let's add a couple print statements and see what's happening.
Version 1:
class Rectangle:
def __init__(self, length, width, **kwargs):
self.length = length
self.width = width
print(f"in rectangle. length = {self.length}, width = {self.width}")
def area(self):
return self.length * self.width
class Square(Rectangle):
def __init__(self, length, **kwargs):
print("in square")
super().__init__(length = length, width = length, **kwargs)
class Triangle:
def __init__(self, base, height, **kwargs):
print("in triangle")
self.base = base
self.height = height
super().__init__(**kwargs)
def tri_area(self):
return 0.5 * self.base * self.height
class Pyramid(Square, Triangle):
def __init__(self, base, slant, **kwargs):
print("in pyramid")
kwargs["height"] = slant
kwargs["length"] = base
super().__init__(base = base, **kwargs)
def surf_area(self):
print(f"area : {super().area()}")
print(f"tri_area : {super().tri_area()}")
return super().area() + 4 * super().tri_area()
p = Pyramid(2,4)
p.surf_area()
Output:
in pyramid
in square
in rectangle. length = 2, width = 2
area : 4
#and then an error, note that it occurs when calling super().tri_area()
#traceback removed for brevity.
AttributeError: 'Pyramid' object has no attribute 'base'
I suspect this already breaks some assumptions you had about how the code runs. Notice that the triangle's init was never called. But let's get rid of the parts that work fine, and add an additional print statement. I will also take the liberty of calling it with a different value for first argument, something that pops out easier.
Version 2:
class Rectangle:
def __init__(self, length, width, **kwargs):
self.length = length
self.width = width
print(f"in rectangle. length = {self.length}, width = {self.width}")
print(f"kwargs are: {kwargs}")
class Square(Rectangle):
def __init__(self, length, **kwargs):
print("in square")
super().__init__(length = length, width = length, **kwargs)
class Triangle:
def __init__(self, base, height, **kwargs):
print("in triangle")
self.base = base
self.height = height
super().__init__(**kwargs)
def tri_area(self):
return 0.5 * self.base * self.height
class Pyramid(Square, Triangle):
def __init__(self, base, slant, **kwargs):
print("in pyramid")
kwargs["height"] = slant
kwargs["length"] = base
super().__init__(base = base, **kwargs)
def surf_area(self):
print(f"tri_area : {super().tri_area()}")
return super().tri_area()
p = Pyramid(10000,4)
p.surf_area()
Output:
in pyramid
in square
in rectangle. length = 10000, width = 10000
kwargs are: {'base': 10000, 'height': 4}
#error with traceback
AttributeError: 'Pyramid' object has no attribute 'base'
Bottom line: the kwargs holds a key with the name base, but this has no relation to self.base. However, my recommendation is to get rid of the whole class structure, and spend some time playing around with any basic function, get rid of the extra stuff.
Say, a demonstration:
def some_func(a, b, **kwargs):
print(a, b)
print(kwargs)
some_func(1, 2)
some_func(1, 2, c=42)
some_func(a=1, c=42, b=2)
def other_func(a, b, **look_at_me):
print(a, b)
print(look_at_me)
other_func(1, 2)
other_func(1, 2, c=42)
other_func(a=1, c=42, b=2)
These two chunks produce the same outputs. No magic here.
Output:
1 2
{}
1 2
{'c': 42}
1 2
{'c': 42}
When you added the classes into the mix, and inheritance, there's too many things happening at once. It is easier to miss what happens, so it's a good idea to use smaller code samples.

SierpinskiTriangle() takes exactly 1 argument (4 given)

I'm trying to initialize sT from an imported module. And getting the error:
sT = SierpinskiTriangle(self.dimensions, 50000, 0.5, vertices)
TypeError: SierpinskiTriangle() takes exactly 1 argument (4 given)
and I'm not really sure why or what I've done wrong.
sT = SierpinskiTriangle(self.dimensions, 50000, 0.5, vertices)
And I've imported this from another file:
class Fractal(Canvas, Point):
def __init__(self, dimensions, num_points, ratio, vertices):
self.dimensions = dimensions
self.num_points = num_points
self.r = ratio
self.vertices = vertices
def frac_x(self, r):
return int((self.dimensions["max_x"] - \
self.dimensions["min_x"]) * r) + \
self.dimensions["min_x"]
def frac_y(self, r):
return int((self.dimensions["max_y"] - \
self.dimensions["min_y"]) * r) + \
self.dimensions["min_y"]
def SierpinskiTriangle(Fractal):
def __init__(self, dimensions, num_points, ratio, vertices):
Fractal.__init__(self, dimensions, num_points, ratio, vertices)
Edit, here's the Point class:
class Point(object):
def __init__(self, x = 0.0, y = 0.0):
self.x = float(x)
self.y = float(y)
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
#property
def y(self):
return self._y
#y.setter
def y(self, value):
self._y = value
def dist(self, secondPoint):
#get the self x values from self.x and the values
#of the seecond point from secondPoint.x
#same with y
dist = math.sqrt(((self.x - secondPoint.x)**2)+ ((self.y - secondPoint.y)**2))
return dist
def midpt(self, secondPoint):
#same as the dist
midpointx = (self.x + secondPoint.x)/2
midpointy = (self.y + secondPoint.y)/2
midpoint = Point(midpointx,midpointy)
return midpoint
def __str__(self):
return "({},{})".format(self.x,self.y)
I hope this also helps clarify things. I don't have the Canvas class because it is a part of Tkinter.
You used def instead of class for SierpinskiTriangle which means it only takes one argument (Fractal) instead of treating Fractal as its super class.
Change that to class like below and it will take 4 arguments.
class SierpinskiTriangle(Fractal):
def __init__(self, dimensions, num_points, ratio, vertices):
Fractal.__init__(self, dimensions, num_points, ratio, vertices)

Multiple constructors in python

How to combine the following 2 classes into one class, Rectangle, so that a Rectangle object can be created either by rect = Rectangle(side_a, side_b) or rect = Rectangle(side_a, area)?
class Rectangle1:
def __init__(self, side_a, side_b):
self.side_a = side_a
self.side_b = side_b
self.area = self.side_a * self.side_b
class Rectangle2:
def __init__(self, side_a, area):
self.side_a = side_a
self.area = area
self.side_b = self.area / side_a
As demonstrated here.
class Rectangle:
def __init__(self, a, b):
""" Create a new rectangle with sides of length a and b.
"""
self.side_a = side_a
self.side_b = side_b
self.area = self.side_a * self.side_b
#classmethod
def from_sides(cls, a, b):
return cls(a, b)
#classmethod
def from_area(cls, a, o):
return cls(a, o/a)
You can then create rectangles as
r1 = Rectangle.from_sides(s1, s2)
r2 = Rectangle.from_area(s1, a)
You cannot overload methods with methods of the same name. Well, you can, but only the last one is then visible.
The other option is keyword-only arguments.
With Python 3, you could write:
class Rectangle:
def __init__(self, side_a, *, side_b=None, area=None):
self.side_a = side_a
if side_b is None and area is None:
raise Exception("Provide either side_b or area")
if side_b is not None and area is not None:
raise Exception("Provide either side_b or area, not both")
if side_b is not None:
self.side_b = side_b
self.area = self.side_a * self.side_b
else:
self.area = area
self.side_b = self.area / side_a
using * in the middle forces the user to use keyword argument passing from that point, not allowing positionnal, which prevents the mistakes. And the (rather clumsy) manual checking for None logic ensures that one and only one keyword parameter is passed to the constructor. The inside is complex, but the interface is safe to use, that's the main point here.
r = Rectangle(10,area=20)
r2 = Rectangle(10,side_b=20)
r3 = Rectangle(10,20) # doesn't run, need keyword arguments
This may not be what you're looking for, but here's what I came up with:
class Rectangle:
def __init__(self, side_a, side_b = None, area = None):
self.side_a = side_a
if area == None:
self.area = side_a * side_b
self.side_b = side_b
else:
self.side_b = area / side_a
self.area = area
You could do
class Combined:
def __init__(self, a, b=None, area=None):
self.a = a
self.area = self.a * b if b else area
self.b = self.area / self.a
I would share a basic example to use default as well as parametrized constructor in python. You need to declare as well as assign variable in function definition itself for default constructor.
class Person:
def __init__(self,name="",age=0):
self.name=name
self.age=age
def __int__(self,name,age):
self.name=name
self.age=age
def showdetails(self):
print("\nName is "+str(self.name)+" and age is "+str(self.age))
p1=Person("Sam",50)
p1.showdetails()
p2=Person()
p2.showdetails()
Output:
Name is Sam and age is 50
Name is and age is 0

Categories

Resources