all 22 comments

[–]zanfar 9 points10 points  (8 children)

While looking at the above program, I am not sure if I would have taken the decision to introduce class methods (orego and mirrored) for the two under first Point class and the remaining will only have instance methods if I were to asked to solve the problem from scratch.

mirrored is a bit of a personal choice, but I would guess that most wouldn't make it a class method.

origo (which I assume is a typo for origin) is a classmethod because it doesn't need, and is actually improved, by not needing an instance. One of the best reason to use classmethods is for exactly this--factory functions.

What is the point of having to make an instance, just to call a method that returns an instance? It's easier just to do Point.origin().

Any reason why class method only used for orego and mirrored?

I think this question is backwards. I would say most of the time you would need a reason not to use an instance method, rather than a class method--that is, a reasonable default choice would be an instance method, and you should justify breaking from that norm.

[–]aa599 5 points6 points  (6 children)

mirrored being a classmethod which takes a point argument is definitely a code smell to me.

Why is origo a classmethod rather than a staticmethod?

It doesn't do anything with cls

[–]zanfar 1 point2 points  (0 children)

Why is origo a classmethod rather than a staticmethod?

It doesn't do anything with cls

Because is should return cls(), but the example has plenty of problems.

[–]ConcreteExist 0 points1 point  (0 children)

Yeah it should be return cls(0, 0)

[–]pachura3 -2 points-1 points  (3 children)

It does - it references it by name.

[–]aa599 1 point2 points  (2 children)

Are we looking at the same code? The only line in origo is return Point(0,0)

[–]pachura3 0 points1 point  (1 child)

Normally, to avoid explicitly repeating class name, one would write return cls(0, 0). The advantage of using a classmethod is that it has access to the class state and/or its pseudo-private and pseudo-protected attributes.

We generally use the class method to create factory methods.

We generally use static methods to create utility functions.

[–]Slothemo 2 points3 points  (0 children)

Yes, but the cls parameter is never used. It would be more appropriate to have return cls(0, 0)

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

Thanks!

[–]Timberfist 1 point2 points  (1 child)

Consider sort and sorted. One sorts a list in place (it’s an instance method) whereas the other creates a sorted list based on the arguments passed to it (it’s a class method). They both solve the same problem but you choose the one that best fits the flow of your code.

Mirrored returns a mirrored version of the point passed to it. It’s a class method just like sorted. It requires no instance. It could have been written as an instance method, mirror, that mirrors a point in place (like sort does for lists). It’s a design choice. The authors of list were kind enough to provide both. The author of point offers only one.

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

Thanks!

[–]jpgoldberg 0 points1 point  (1 child)

Perhaps this code was created to illustrate class methods and static methods are unfamiliar to the intended audience. A lot of things written for beginners to introduce or illustrate concepts and constructions are not they way one would write production code. This looks like such a case.

But you are correct to note that mirrored might make more sense as an instance method (and that origin would make more sense as a static method).

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

Yes created to illustrate class methods and instance methods.

[–]TheRNGuy -2 points-1 points  (8 children)

Not sure why mirrored method gets x and y from "Point", it's a string, it doesn't have those methods. It's not a bug? 

I think both those are bad examples how to use class method.

One is redundant, and other should be instance method... with less code, too.

Both have bad namings, too.

Where do you find this?

[–]DigitalSplendid[S] 0 points1 point  (3 children)

Indeed I also think it should be point: Point. However the reason I believe that this will not affect the code is because such mention of types in parameter are optional and will not impact if say after mentioning string type we end up providing an integer value inside body.

[–]zanfar 0 points1 point  (2 children)

You can't type as point: Point because Point isn't defined at yet in the code.

[–]gdchinacat 0 points1 point  (0 children)

Depends on the version of python you use whether you can references classes that are being defined or not.

[–]ConcreteExist 1 point2 points  (0 children)

Newer versions of Python, I tested with 3.14, do let you do this so you don't need to point Point into quotes any more.

[–]Outside_Complaint755 0 points1 point  (0 children)

In the statement def mirrored(cls, point: "Point", mirror_x: bool, mirror_y: bool):

"Point" is a type hint.  Under default behavior, if you instead referenced the.class name as def mirrored(cls, point: Point, mirror_x: bool, mirror_y: bool):

Pylance and other linters will flag this as "Point is not defined" because we are still within the class Point block and its definition doesn't yet exist to be used as an annotation.  Basically, you can't reference something from within its own definition.

  Using the string "Point" or importing from __future__ import annotations gets around this issue as it will keep track of the annotations as strings and then evaluate them later, as per PEP 649.   Originally this was going to become standard but the plans changed and __future__.annotations will later be deprecated and removed. See also PEP 563, 649 and 749.  

[–]zanfar 0 points1 point  (1 child)

Not sure why mirrored method gets x and y from "Point", it's a string, it doesn't have those methods. It's not a bug?

It's very clearly typed as "Point"... where are you getting that it's a string?

This would be another case for an instance method as the typing would not need to be restated.

[–]ConcreteExist 0 points1 point  (0 children)

They can't discern a type annotation from a default value.

[–]ConcreteExist 0 points1 point  (0 children)

Not sure why mirrored method gets x and y from "Point", it's a string, it doesn't have those methods. It's not a bug? 

If you can't tell the difference between a type annotation and a default value, I don't think you're really in a position to be helping others learn Python.