Using static methods in python is best practice

When and how are static methods supposed to be used in python? We have already installed using the class method, since the factory method for creating an instance of an object should be avoided when possible. In other words, it is not recommended to use class methods as an alternative constructor (see Factory method for python object - best practice ).

Suppose I have a class used to represent entity data in a database. Imagine this is a dict object containing field names and field values, and one of the fields is an identification number that makes the data unique.

 class Entity(object): def __init__(self, data, db_connection): self._data = data self._db_connection 

Here my __init__ method takes a dict object data object. Suppose I only have an identification number and I want to create an instance of Entity . First I will need to find the rest of the data, and then create an instance of my Entity object. From my previous question, we found that, whenever possible, the use of a class method as a factory method should be avoided.

 class Entity(object): @classmethod def from_id(cls, id_number, db_connection): filters = [['id', 'is', id_number]] data = db_connection.find(filters) return cls(data, db_connection) def __init__(self, data, db_connection): self._data = data self._db_connection # Create entity entity = Entity.from_id(id_number, db_connection) 

The above is an example of what not to do, or at least not to do if there is an alternative. Now I'm curious about editing my class method, so this is a more useful method, and the factory method is a valid solution. In other words, whether the following example is consistent with best practice using static methods.

 class Entity(object): @staticmethod def data_from_id(id_number, db_connection): filters = [['id', 'is', id_number]] data = db_connection.find(filters) return data # Create entity data = Entity.data_from_id(id_number, db_connection) entity = Entity(data) 

Or it makes sense to use an autonomous function to search for entity data from an identification number.

 def find_data_from_id(id_number, db_connection): filters = [['id', 'is', id_number]] data = db_connection.find(filters) return data # Create entity. data = find_data_from_id(id_number, db_connection) entity = Entity(data, db_connection) 

Note. I do not want to change my __init__ method. People used to propose making my __init__ method look like this __init__(self, data=None, id_number=None) , but there would be 101 ways to find entity data, so I would prefer to keep this logic separate. It makes sense?

+11
python coding-style static-methods
Feb 22
source share
3 answers

When and how are static methods supposed to be used in python?

Glib answer: Not very often.

An even, but not entirely useless answer: when they make your code more readable.




First, take a workaround in the docs :

Static methods in Python are similar to those that exist in Java or C ++. Also see classmethod() for an option that is useful for creating alternative class constructors.

So, when you need a static method in C ++, you need a static method in Python, right?

Oh no.

There are no functions in Java, just methods, so you create pseudo-classes that are just static method associations. The way to do the same in Python is to simply use free functions.

This is pretty obvious. However, it is a good Java style to look as difficult as possible for the corresponding class to insert a function into it, so you can avoid writing these pseudo-classes while doing the same thing, this is a bad Python style - use free functions again - and it is much less obvious.

C ++ does not have the same restriction as Java, but many C ++ styles are pretty similar. (On the other hand, if you are a Modern C ++ programmer who has learned the words “free functions that are part of the class interface”, your instincts for “where static methods are useful” are probably pretty good for Python.)




But if you come to this from the first principles, and not from another language, there is an easier way to look at things:

A @staticmethod is basically just a global function. If you have a function foo_module.bar() that would be more readable for some reason, if it were written as foo_module.BazClass.bar() , make it @staticmethod . If not, do not do this. That is really all that is needed. The only problem is creating your instincts for what is more readable for the idiomatic Python programmer.

And of course, use @classmethod when you need access to the class, but not instance constructors-alternators - this is the paradigm for this, as the docs imply. Although you can often model @classmethod with @staticmethod , just explicitly referring to the class (especially if you don't have a large subclass), you shouldn't.




Finally, going to your specific question:

If the only reason customers should ever look up data by identifier is to create an Entity that sounds like an implementation detail that you shouldn't disclose, and also makes client code more complex. Just use the constructor. If you do not want to change your __init__ (and you are right that there are good reasons for which you do not want to), use @classmethod as an alternative constructor: Entity.from_id(id_number, db_connection) .

On the other hand, if this search is something that is inherently useful for clients in other cases that have nothing to do with the Entity construct, it looks like it has nothing to do with the Entity class (or at least no more than anything else in one module). So just make it a free function.

+16
Feb 22 '13 at 19:42
source share

The answer to a related question specifically says this:

The @classmethod method is an idiomatic way to make an "alternative constructor" - there are examples all over stdlib-itertools.chain.from_iterable, datetime.datetime.fromordinal, etc.

So, I do not know how you understood that using the class method is inherently bad. I really like the idea of ​​using classmethod in your specific situation, as it makes the following code and uses api easily.

An alternative would be to use the default constructor arguments:

 class Entity(object): def __init__(self, id, db_connection, data=None): self.id = id self.db_connection = db_connection if data is None: self.data = self.from_id(id, db_connection) else: self.data = data def from_id(cls, id_number, db_connection): filters = [['id', 'is', id_number]] return db_connection.find(filters) 

I prefer the version of classmethod that you originally wrote. Moreover, data rather ambiguous.

+10
Feb 22 '13 at 6:08
source share

Your first example makes the most sense to me: Entity.from_id pretty short and Entity.from_id .

It avoids the use of data in the following two examples, which do not describe what is returned; data used to build Entity . If you want to indicate that data used to create Entity , you can call your method something like Entity.with_data_for_id or the equivalent entity_with_data_for_id function.

Using a verb such as find can also be quite confusing, as it gives no indication of the return value - what is the function that should be executed when the data is detected? (Yes, I understand that str has a find method, is it not better called index_of ? But then there is also index ...) This reminds me of the classics:

find x

I always try to think what name will point to someone (a) without knowledge of the system, and (b) knowledge of other parts of the system - not to say that I am always successful!

+6
Feb 22 '13 at 6:08
source share



All Articles