Python referencing static methods within a class - best practise - python

This is just an "Is there a better way of doing x?" question about #staticmethod functions in classes using python.
I have the following:
class my_class():
#staticmethod
def function_a():
print("hello")
#staticmethod
def function_b():
my_class.function_a()
Obviously with static classes you have no "self" reference, but is there another way to reference functions inside a class without using the class name "my_class.xxxxx"?
Most other languages have a different version, for example php has $this-> for inheritance and self:: for static.

my_class.function_b should be a classmethod:
#classmethod
def function_b(cls):
cls.function_a()
classmethods get passed a reference to the class that they are called on (or the class of the instance that they are called on) as the first argument rather than the usual self.

Related

Extending a class in Python inside a decorator

I am using a decorator to extend certain classes and add some functionality to them, something like the following:
def useful_stuff(cls):
class LocalClass(cls):
def better_foo(self):
print('better foo')
return LocalClass
#useful_stuff
class MyClass:
def foo(self):
print('foo')
Unfortunaltely, MyClass is no longer pickleable due to the non global LocalClass
AttributeError: Can't pickle local object 'useful_stuff.<locals>.LocalClass'
I need to pickle my classes. Can you recommend a better design?
Considering that there can be multiple decorators on a class, would switching to multiple inheritance by having MyClass inherit all the functionality be a better option?
You need to set the metadata so the subclass looks like the original:
def deco(cls):
class SubClass(cls):
...
SubClass.__name__ = cls.__name__
SubClass.__qualname__ = cls.__qualname__
SubClass.__module__ = cls.__module__
return SubClass
Classes are pickled by using their module and qualname to record where to find the class. Your class needs to be found in the same location the original class would have been if it hadn't been decorated, so pickle needs to see the same module and qualname. This is similar to what funcutils.wraps does for decorated functions.
However, it would probably be simpler and less bug-prone to instead add the new methods directly to the original class instead of creating a subclass:
def better_foo(self):
print('better_foo')
def useful_stuff(cls):
cls.better_foo = better_foo
return cls

Why does a method within a class work even if not marked with #classmethod or #staticmethod?

I'm new to Python and just learning about its implementation of objects/classes. I understand the difference between an instance method, class method, and static method, I think, but what I don't understand is why a method that has not been decorated as a #classmethod or #staticmethod can be called from the class itself.
My very (very) basic example:
class TestClass:
def __init__(self):
pass
#staticmethod
def print_static_stuff(stuff):
print(stuff)
def print_stuff(stuff):
print(stuff)
TestClass.print_stuff("stuff") # prints "stuff"
TestClass.print_static_stuff("static stuff") # prints "static stuff"
The method print_stuff() seems to act as a static method when called on the class, taking the argument as it's single parameter and not the class (cls). Why can the method not decorated with #staticclass be called on the class? Is this by design or just a weird side-effect, and why? From what I've learned so far, Python, by design, has few to no "weird side-effects".
The first parameter being named self is merely a convention. The instance will be passed as the first positional argument independent of what you've named it (in this case you've called it stuff).

How to use self parameter, #staticmethod keyword inside a class and its methods

I have a python class which has multiple methods. I have defined my methods via #staticmethod instance and I want to call other methods of my class from inside my main function(main_function). I think I need self parameter for calling my other functions from my main function and I want to pass this parameter to my main_function when I create an instance of my class.
class myclass:
#staticmethod
def function1(param1)
print "function1"
#staticmethod
def main_function(self, param1)
function1(param1)
my_object = myclass()
my_object.main_function(param1)
I got this error:
TypeError: main_function() takes exactly 2 arguments (1 given)
The problem is that I have not self parameter when I create my instance. I tried to remove #staticmethod keyword from my method definition and remove all self parameter using, but this does not work.
Only use #staticmethod if you are creating a function that you'd normally want to tie to specific classes but do not need any other context. For example, the str.maketrans() function is a static method because it is a utility function you'd often use when working with strings, namespacing it to the already-existing str type (which pre-exists as a class) makes sense there.
You appear to be using classes as a namespace instead. Don't do that. Use a module for your functions, and you don't have to worry about the special scoping rules that apply to classes. Only use a class when you need to bundle state with functionality.
If you insist on using classes with static methods anyway, you are stuck with hardcoding the class name everywhere:
class myclass:
#staticmethod
def function1(param1)
print "function1"
#staticmethod
def main_function(param1)
# Want to use other functions in this class? Then you will
# have to use the full name of the class as a prefix:
myclass.function1(param1)
You could make use of classmethods instead so you have a reference to the class object:
class myclass:
#staticmethod
def function1(param1)
print "function1"
#classmethod
def main_function(cls, param1)
# Now you can use the `cls` reference to access other attributes
cls.function1(param1)
This has the added advantage that you can use inheritance.
However, using a module is the correct way to organise a set of functions into a namespace. Put everything into a my_module.py file in your package, and use importing;
import my_module
my_module.main_function(param1)
Now all globals in my_module are bundled into one module object, and no prefixing or cls references are needed.

Python refer to class from within class

I've become aware of #staticmethod - next question, are you supposed to use the class name to refer to these methods from within the class?
class C:
#staticmethod
def imstatic():
print("i'm static")
#staticmethod
def anotherstatic():
# Is this the proper python way?
C.imstatic()
#staticmethod
def brokenstatic():
# This doesn't work..
self.imstatic()
Yes, as you don't have any other reference to the class from within a static method. You could make these class methods instead, using the classmethod decorator:
class C:
#staticmethod
def imstatic():
print("i'm static")
#classmethod
def anotherstatic(cls):
cls.imstatic()
A class method does have a reference to the class.
If you need to refer to the class within a static method you should probably be using a classmethod instead:
class C:
#staticmethod
def imstatic():
print("i'm static")
#classmethod
def imclass(cls):
cls.imstatic()
In the same way that instance methods are "magically" given a reference to the instance as the first argument, class methods are given a reference to the class. You can call them either from an instance or from the class directly, for example both of the following are valid and have the same behavior:
C().imclass()
C.imclass()
That being said, if you do still want to use a static method your current approach is correct, just refer to the class by name.
If you always want to call the static method of that specific class, yes, you must specify it by name. If you want to support overriding the static methods, what you want is a classmethod instead: it passes the class on which the method is being called as the first parameter, analogous to self on regular instance methods, so you can call the overridden method. In general I'd suggest using classmethods.

Is it okay to pass self to an external function

I have a class, A, which is inherited by a bunch of other classes. Some of these have a few functions which are similar and it would be nice to have those functions defined somewhere else and called by the classes that need them. But those functions call functions defined in the super class.
class A():
def imp_func(*args):
# called by the child class functions
Class B(A):
def common_func(self):
# some stuff
self.imp_func(*args)
So I have created my helper functions which take the self object as an argument and I can call the imp_func from inside the helper functions.
def helper_func(obj, some_args):
# some common stuff
obj.imp_func(*args)
class B(A):
def common_func(self):
# unique stuff
helper_func(self, some_args)
This solves the problem.
But should I be doing this? Is this Pythonic?
There is no problem with that whatsoever - self is an object like any other and may be used in any context where object of its type/behavior would be welcome.
In Python, as exemplified by the standard library, instances of self get passed to functions (and also to methods, and even operators) all the time.

Categories

Resources