On 11Aug2019 22:58, James Hartley <jjhart...@gmail.com> wrote:
I am lacking in understanding of the @staticmethod property.
Explanation(s)/links might be helpful. I have not found the descriptions
found in the Internet wild to be particularly instructive.
You have received some answers; to me they seem detailed enough to be
confusing.
I think of things this way: what context does a method require? Not
everything needs the calling instance.
Here endeth the lesson.
============
All this stuff below is examples based on that criterion:
Here's a trite example class:
class Rectangle:
def __init__(self, width, height):
self.width=width
self.height = height
Most methods do things with self, and are thus "instance methods", the
default. They automatically receive the instance used to call them as
the first "self" argument.
def area(self):
return self.width * self.height
They need "self" as their context to do their work.
Some methods might not need an instance as context: perhaps they return
information that is just based on the class, or they are factory methods
intended to return a new instance of the class. Then you might use a
@classmethod decorator to have the calling instance's class as the
context.
@classmethod
def from_str(cls, s):
width, height = parse_an_XxY_string(s)
return cls(width, height)
And some methods do not need the class or the instance to do something
useful:
@staticmethod
def compute_area(width, height):
return width * height
and so we don't give them the instance or the class as context.
Now, _why_?
Instance methods are obvious enough - they exist to return values
without the caller needing to know about the object internals.
Class methods are less obvious.
Consider that Python is a duck typed language: we try to arrange that
provided an object has the right methods we can use various different
types of objects with the same functions. For example:
def total_area(flat_things):
return sum(flat_thing.area() for flat_thing in flat_things)
That will work for Rectangles and also other things with .area()
methods. Area, though, is an instance method.
Class methods tend to come into their own with subclassing: I
particularly use them for factory methods.
Supposing we have Rectangles and Ellipses, both subclasses of a
FlatThing:
class FlatThing:
def __init__(self, width, height):
self.width=width
self.height = height
@classmethod
def from_str(cls, s):
width, height = parse_an_XxY_string(s)
return cls(width, height)
class Rectangle(FlatThing):
def area(self):
return self.width * self.height
class Ellipse(FlatThing):
def area(self):
return self.width * self.height * math.PI / 4
See that from_str? It is common to all the classes because they can all
be characterised by their width and height. But I require the class for
context in order to make a new object of the _correct_ class. Examples:
rect = Rectangle.from_str("5x9")
ellipse = Ellipse.from_str("5x9")
ellispe2 = ellipse.from_str("6x7")
Here we make a Rectangle, and "cls" is Rectangle when you call it this
way. Then we make an Ellipse, and "cls" is Ellipse when called this way.
And then we make another Ellipse from the first ellipse, so "cls" is
again "Ellipse" (because "ellipse" is an Ellipse).
You can see that regardless of how we call the factory function, the
only context passed is the relevant class.
And in the last example (an Ellipse from an existing Ellipse), the class
comes from the instance used to make the call. So we can write some
function which DOES NOT KNOW whether it gets Ellipses or Rectangles:
def bigger_things(flat_things):
return [ flat_thing.from_str(
"%sx%s" % (flat_thing.width*2, flat_thing.height*2))
for flat_thing in flat_things
]
Here we could pass in a mix if Rectangles or Ellipses (or anything else
with a suitable from_str method) and get out a new list with a matching
mix of bigger things.
Finally, the static method.
As Peter remarked, because a static method does not have the instance or
class for context, it _could_ be written as an ordinary top level
function.
Usually we use a static method in order to group top level functions
_related_ to a specific class together. It also helps with imports
elsewhere.
So consider the earlier:
@staticmethod
def compute_area(width, height):
return width * height
in the Rectangle class. We _could_ just write this as a top level
function outside the class:
def compute_rectangular_area(width, height):
return width * height
Now think about using that elsewhere:
from flat_things_module import Rectangle, Ellipse, compute_rectangular_area
area1 = compute_rectangular_area(5, 9)
area2 = Rectangle.compute_area(5, 9)
area3 = Ellipse.compute_area(5, 9)
I would rather use the forms of "area2" and "area3" because it is clear
that I'm getting an area function from a nicely named class. (Also, I
wouldn't have to import the area function explicitly - it comes along
with the class nicely.)
So the static method is used to associate it with the class it supports,
for use when the caller doesn't have an instance to hand.
Cheers,
Cameron Simpson <c...@cskk.id.au>
_______________________________________________
Tutor maillist - Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor