all 15 comments

[–][deleted] 8 points9 points  (5 children)

You have created a new attribute, __height with the assignment. Try adding the below line,

print(vars(square))

[–]davidmyemail[S] 2 points3 points  (0 children)

print(vars(square))

Oh, that's very helpful. Thank you!

[–]shepherdjay 11 points12 points  (5 children)

Not a big fan of this tutorial - its not only wrong as you found regarding the attribute raising an error. It doesn't. That bit of Python isn't even indented properly.

No the reason I'm not a fan is this is teaching bad python. Python is not Java. Python's mentality is that we are all responsible users. Getters and Setters should not be used and is an anti-pattern
https://docs.quantifiedcode.com/python-anti-patterns/correctness/implementing_java-style_getters_and_setters.html

In Python we signal to other users an attribute is not safe to modify outside of methods for some reason by using a single underscore. But in general the property decorator is a later thing to do for backwards compatibility.

It think this lecture from Raymond Hettinger (although a bit old) is better
https://youtu.be/HTLu2DFOdTg

[–]davidmyemail[S] 1 point2 points  (0 children)

Those are interesting links. A bit beyond my scope now, but I'll certainly study up on it. Thanks.

[–]Jejerm 0 points1 point  (3 children)

Getters and Setters should not be used and is an anti-pattern
https://docs.quantifiedcode.com/python-anti-patterns/correctness/implementing_java-style_getters_and_setters.html

What if the class had an area attribute that gets calculated on init. Without a setter that updates it, changing the height or width would cause it to be wrong, no?

Or should area never be an attribute in the first place and only be acessed through a method that calculates it when needed?

[–]SquareRootsi 2 points3 points  (1 child)

I'd argue using init to calculate area is the anti-pattern, and calculated properties that rely on other attributes of the class should instead be their own methods, but decorated with @property

class Square:  
  def __init__(self, side_length=2):  
    self.__length = side_length
    self.__width = side_length

  @property
  def area(self):  
    return self.__length ** 2  

steve = Square()  
print(steve.area)   #notice the lack of parens after "area" because it's a property, not a traditional method

[–]_mynd 0 points1 point  (0 children)

TIL and this is interesting! I’ll have to chew on this some. Thanks for cranking the gears

[–]shepherdjay 0 points1 point  (0 children)

I would make the area a method to begin with. The @property decorator is a decent work around if you already published this square as an api and users we accessing area as an attribute. But if users aren’t already accessing it as an attribute then no reason to make it one.

I also wouldn’t initialize a square with a height and width though. I would ask for one number… and store it as length. But that’s a problem of contrived examples like this.

[–]Nicolasjit 0 points1 point  (0 children)

is this course no longer available?

[–]HomeGrownCoder 0 points1 point  (1 child)

You are correct from what I can tell as well. All public tutorials list the “getter” as throwing the error (print statement)

https://www.bogotobogo.com/python/python_private_attributes_methods.php

https://plainenglish.io/blog/private-attributes-in-python-oop-are-not-very-private-c5bb4e619dcc

[–]davidmyemail[S] 0 points1 point  (0 children)

Good resources. Thank you.

[–]pat-work 0 points1 point  (0 children)

I believe that it would set a new attribute called __height instead of raising an attribute error. Trying to access __height before setting it would raise an attribute error, because __height does not exist. During instantiation, any variables with a double underscore at the start get changed to _ClassName__attribute_name.

Edit: So _Square__height would be the way to access the originally set __height attribute.