Classmethod and staticmethod in python

Методы класса (classmethod) и статические методы (staticmethod)

Мы продолжаем тему методов в ООП. До сих пор мы с вами определяли методы просто как функции внутри класса, например:

class Vector: def __init__(self, x, y): self.x = x self.y = y def get_coord(self): return self.x, self.y

И у каждого такого метода, как правило, первым идет параметр self – ссылка на экземпляр класса, из которого метод был вызван:

v = Vector(10, 20) coord = v.get_coord() print(coord)

В данном случае, при вызове метода get_coord () параметр self будет вести на объект v класса Vector. Об этом мы с вами уже говорили. Также отмечали, что при вызове такого метода напрямую из класса нужно явно указывать первый аргумент self:

Так вот, в Python помимо таких «стандартных» методов можно задавать методы уровня класса и статические методы с помощью встроенных декораторов:

@classmethod и @staticmethod

Давайте я поясню на простом примере, что они значат. Добавим в наш класс Vector два атрибута:

class Vector: MIN_COORD = 0 MAX_COORD = 100 .
@classmethod def validate(cls, arg): return cls.MIN_COORD  arg  cls.MAX_COORD

который проверяет, попадает ли значение arg в диапазон [MIN_COORD; MAX_COORD]. Обратите внимание, у методов класса (когда мы используем декоратор classmethod) первым параметром идет cls – ссылка на класс, а не self – ссылка на объект класса. Это означает, что данный метод может обращаться только к атрибутам текущего класса, но не к локальным свойствам его экземпляров. Мало того, этот метод можно теперь напрямую вызывать из класса, не передавая ссылку на экземпляр, как это было при вызове обычных методов через класс:

res = Vector.validate(5) print(res)

Здесь пользователь класса Vector может совершенно спокойно вызывать метод validate(), не создавая никаких объектов. Но «платой» за это является ограниченность метода: он может работать только с атрибутами класса, но не объекта, что, в общем то, естественно, так как у него изначально нет ссылки на объект. Во всем остальном этот метод работает абсолютно также, как и любой другой метод, объявленный в классе.

Давайте мы им воспользуемся и вызовем внутри класса для проверки корректности координат x, y:

def __init__(self, x, y): self.x = self.y = 0 if Vector.validate(x) and Vector.validate(y): self.x = x self.y = y

Обратите внимание, мы здесь обращаемся к методу класса через пространство имен Vector. Но также можем прописать и self:

if self.validate(x) and self.validate(y):

В этом случае интерпретатор Python сам подставит нужный класс в параметр cls данного метода, так как экземпляр содержит информацию о классе, от которого был образован.

Наконец, третий тип методов – статические методы, определяются декоратором @staticmethod. Это методы, которые не имеют доступа ни к атрибутам класса, ни к атрибутам его экземпляров, то есть, некая независимая, самостоятельная функция, объявленная внутри класса. Обычно, это делают для удобства, т.к. их функционал так или иначе связан с тематикой класса.

Например, в нашем классе Vector можно объявить такой статический метод, который бы вычислял квадратичную норму вектора (длину вектора в квадрате):

@staticmethod def norm2(x, y): return x*x + y*y

Здесь нет никаких скрытых параметров, которые бы автоматически заполнялись интерпретатором языка. Только те, что мы прописываем сами. Я указал два параметра x, y, по которым вычисляется квадрат длины радиус-вектора. То есть, это некая вспомогательная, сервисная функция, связанная с векторами, для вычисления квадратичной нормы любого радиус-вектора. Воспользоваться этим методом можно как вне класса:

def __init__(self, x, y): self.x = self.y = 0 if self.validate(x) and self.validate(y): self.x = x self.y = y print(Vector.norm2(self.x, self.y))

Либо, также обратиться к этому методу через self:

print(self.norm2(self.x, self.y))

Подведем итог различных типов методов в классах. Обычные методы, как правило, вызываются из экземпляров классов и работают с атрибутами экземпляров и атрибутами классов. Методы классов обычно вызываются через класс, реже через его экземпляры и имеют доступ только к атрибутам самого класса, в котором объявлены. Наконец, статические методы – это совершенно изолированные функции, которые работают только с параметрами, прописанными в ней самой и не имеют доступа к атрибутам класса или его экземпляров.

Поэтому, если вам нужен метод, который работает с атрибутами объектов класса, то это обычное определение функций внутри класса с первым параметром self. Если метод работает только с атрибутами класса, то возможно, имеет смысл его определить как метод класса и тогда можно будет вызывать без ссылки на объект этого класса. Третий тип, статические методы часто определяют как вспомогательные, сервисные, связанные с логикой работы самого класса.

Вот общее руководство по выбору этих методов. Надеюсь, из этого занятия вам стало понятно, что такое методы класса и статические методы, а также для чего имеет смысл их использовать.

Видео по теме

Концепция ООП простыми словами

Источник

Python’s @classmethod and @staticmethod Explained

Python is a unique language in that it is fairly easy to learn, given its straight-forward syntax, yet still extremely powerful. There are a lot more features under the hood than you might realize. While I could be referring to quite a few different things with this statement, in this case I’m talking about the decorators @classmethod and @staticmethod . For many of your projects, you probably didn’t need or encounter these features, but you may find that they come in handy quite a bit more than you’d expect. It’s not as obvious how to create Python static methods, which is where these two decorators come in.

In this article I’ll be telling you what each of these decorators do, their differences, and some examples of each.

The @classmethod Decorator

This decorator exists so you can create class methods that are passed the actual class object within the function call, much like self is passed to any other ordinary instance method in a class.

In those instance methods, the self argument is the class instance object itself, which can then be used to act on instance data. @classmethod methods also have a mandatory first argument, but this argument isn’t a class instance, it’s actually the uninstantiated class itself. So, while a typical class method might look like this:

class Student(object): def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name scott = Student('Scott', 'Robinson') 

A similar @classmethod method would be used like this instead:

class Student(object): # Constructor removed for brevity @classmethod def from_string(cls, name_str): first_name, last_name = map(str, name_str.split(' ')) student = cls(first_name, last_name) return student scott = Student.from_string('Scott Robinson') 

This follows the static factory pattern very well, encapsulating the parsing logic inside of the method itself.

The above example is a very simple one, but you can imagine more complicated examples that make this more attractive. Imagine if a Student object could be serialized in to many different formats. You could use this same strategy to parse them all:

class Student(object): # Constructor removed for brevity @classmethod def from_string(cls, name_str): first_name, last_name = map(str, name_str.split(' ')) student = cls(first_name, last_name) return student @classmethod def from_json(cls, json_obj): # parse json. return student @classmethod def from_pickle(cls, pickle_file): # load pickle file. return student 

The decorator becomes even more useful when you realize its usefulness in sub-classes. Since the class object is given to you within the method, you can still use the same @classmethod for sub-classes as well.

The @staticmethod Decorator

The @staticmethod decorator is similar to @classmethod in that it can be called from an uninstantiated class object, although in this case there is no cls parameter passed to its method. So an example might look like this:

class Student(object): # Constructor removed for brevity @staticmethod def is_full_name(name_str): names = name_str.split(' ') return len(names) > 1 Student.is_full_name('Scott Robinson') # True Student.is_full_name('Scott') # False 

Since no self object is passed either, that means we also don’t have access to any instance data, and thus this method can not be called on an instantiated object either.

These types of methods aren’t typically meant to create/instantiate objects, but they may contain some type of logic pertaining to the class itself, like a helper or utility method.

@classmethod vs @staticmethod

The most obvious thing between these decorators is their ability to create static methods within a class. These types of methods can be called on uninstantiated class objects, much like classes using the static keyword in Java.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

There is really only one difference between these two method decorators, but it’s a major one. You probably noticed in the sections above that @classmethod methods have a cls parameter sent to their methods, while @staticmethod methods do not.

This cls parameter is the class object we talked about, which allows @classmethod methods to easily instantiate the class, regardless of any inheritance going on. The lack of this cls parameter in @staticmethod methods make them true static methods in the traditional sense. They’re main purpose is to contain logic pertaining to the class, but that logic should not have any need for specific class instance data.

A Longer Example

Now let’s see another example where we use both types together in the same class:

# static.py class ClassGrades: def __init__(self, grades): self.grades = grades @classmethod def from_csv(cls, grade_csv_str): grades = map(int, grade_csv_str.split(', ')) cls.validate(grades) return cls(grades) @staticmethod def validate(grades): for g in grades: if g < 0 or g > 100: raise Exception() try: # Try out some valid grades class_grades_valid = ClassGrades.from_csv('90, 80, 85, 94, 70') print 'Got grades:', class_grades_valid.grades # Should fail with invalid grades class_grades_invalid = ClassGrades.from_csv('92, -15, 99, 101, 77, 65, 100') print class_grades_invalid.grades except: print 'Invalid!' 
$ python static.py Got grades: [90, 80, 85, 94, 70] Invalid! 

Notice how the static methods can even work together with from_csv calling validate using the cls object. Running the code above should print out an array of valid grades, and then fail on the second attempt, thus printing out «Invalid!».

Conclusion

In this article you saw how both the @classmethod and @staticmethod decorators work in Python, some examples of each in action, and how they differ from each other. Hopefully now you can apply them to your own projects and use them to continue to improve the quality and organization of your own code.

Have you ever used these decorators before, and if so, how? Let us know in the comments!

Источник

Читайте также:  Photoimage tkinter python resize
Оцените статью