Для чего используются методы класса в Python?

595
42

Я преподаю сам Python, и мой последний урок состоял в том, что Python - это не Java, и поэтому я просто потратил некоторое время все мои методы класса в функции.


Теперь я понимаю, что мне не нужно использовать методы класса для того, что я сделал бы с методами static в Java, но теперь я не уверен, когда буду использовать их. Все советы, которые я могу найти о методах класса Python, похожи на новичков, таких как я, должны избегать их, и стандартная документация при их обсуждении наиболее непрозрачна.


Есть ли у кого-нибудь хороший пример использования метода класса в Python или, по крайней мере, может кто-нибудь сказать мне, когда методы класса могут быть разумно использованы?

спросил(а) 2008-09-01T21:16:00+04:00 12 лет, 1 месяц назад
1
Решение
93

Честно? Я никогда не нашел использования для staticmethod или classmethod. Я еще не видел операцию, которая не может быть выполнена с использованием глобальной функции или метода экземпляра.


Было бы иначе, если бы python использовал закрытые и защищенные члены, похожие на Java. В Java мне нужен статический метод, чтобы иметь возможность доступа к частным членам экземпляра, чтобы делать вещи. В Python это редко необходимо.


Обычно я вижу людей, использующих staticmethods и classmethods, когда все, что им действительно нужно делать, это лучше использовать пространства имен на уровне модуля python.

ответил(а) 2008-09-01T21:54:00+04:00 12 лет, 1 месяц назад
509

Методы класса предназначены для тех случаев, когда вам нужно иметь методы, которые не являются специфическими для какого-либо конкретного экземпляра, но по-прежнему связаны с классом. Самое интересное в них заключается в том, что они могут быть переопределены подклассами, что просто невозможно в статических методах Java или в модульных функциях Python.

Если у вас есть класс MyClass и функция уровня модуля, работающая на MyClass (factory, заглушка зависимостей и т.д.), сделайте его classmethod. Затем он будет доступен для подклассов.

ответил(а) 2008-09-01T21:45:00+04:00 12 лет, 1 месяц назад
319

Factory (альтернативные конструкторы) действительно являются классическим примером методов класса.


В принципе, методы класса подходят в любое время, когда вы хотели бы иметь метод, который естественно вписывается в пространство имен класса, но не связан с конкретным экземпляром класса.


В качестве примера, в отличном unipath модуле:


Текущий каталог

    Path.cwd()
      Возвращает текущий текущий каталог; например, Path("/tmp/my_temp_dir"). Это метод класса.

    .chdir()
      Сделайте сам текущий каталог.

Поскольку текущий каталог является широким процессом, метод cwd не имеет конкретного экземпляра, с которым он должен быть связан. Однако изменение cwd в каталог данного экземпляра Path действительно должно быть методом экземпляра.


Hmmm... как Path.cwd() действительно возвращает экземпляр Path, я думаю, его можно было бы считать методом factory...

ответил(а) 2008-09-01T22:08:00+04:00 12 лет, 1 месяц назад
276

Подумайте об этом так: обычные методы помогают скрыть детали отправки: вы можете набрать myobj.foo(), не беспокоясь о том, реализуется ли метод foo() классом объектов myobj или одним из его родительских классов, Методы класса в точности аналогичны этому, но вместо этого вместо этого используется объект класса: они позволяют вам вызывать MyClass.foo(), не беспокоясь о том, что foo() специально используется MyClass, потому что ему нужна его специальная версия или она позволяя родительскому классу обрабатывать вызов.


Методы класса важны, когда вы выполняете настройку или вычисление, которые предшествуют созданию фактического экземпляра, поскольку до тех пор, пока экземпляр не существует, вы, очевидно, не сможете использовать экземпляр в качестве точки отправки для вызовов метода. Хороший пример можно посмотреть в исходном коде SQLAlchemy; взгляните на метод класса dbapi() по следующей ссылке:


https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47


Вы можете видеть, что метод dbapi(), используемый бэкэндом базы данных для импорта библиотеки баз данных, специфичной для поставщика, которая требуется по запросу, является методом класса, потому что она должна выполняться до того, как будут запущены экземпляры конкретного подключения к базе данных но это не может быть простой функцией или статической функцией, потому что они хотят, чтобы она могла вызвать другие поддерживающие методы, которые аналогичным образом должны быть написаны более конкретно в подклассах, чем в их родительском классе. И если вы отправляете функцию или статический класс, тогда вы "забываете" и теряете знания о том, какой класс выполняет инициализацию.

ответил(а) 2010-08-19T15:51:00+04:00 10 лет, 2 месяца назад
212

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


Итак, для этого я использовал переменные класса и декоратор @classmethod.


С моим простым классом Logging я мог бы сделать следующее:


Logger._level = Logger.DEBUG

Тогда, в моем коде, если бы я хотел выплюнуть кучу отладочной информации, мне просто пришлось закодировать


Logger.debug( "this is some annoying message I only want to see while debugging" )

Ошибки могут быть поставлены с помощью


Logger.error( "Wow, something really awful happened." )

В среде "production" я могу указать


Logger._level = Logger.ERROR

и теперь будет выводиться только сообщение об ошибке. Отладочное сообщение не будет напечатано.


Здесь мой класс:


class Logger :
''' Handles logging of debugging and error messages. '''

DEBUG = 5
INFO = 4
WARN = 3
ERROR = 2
FATAL = 1
_level = DEBUG

def __init__( self ) :
Logger._level = Logger.DEBUG

@classmethod
def isLevel( cls, level ) :
return cls._level >= level

@classmethod
def debug( cls, message ) :
if cls.isLevel( Logger.DEBUG ) :
print "DEBUG: " + message

@classmethod
def info( cls, message ) :
if cls.isLevel( Logger.INFO ) :
print "INFO : " + message

@classmethod
def warn( cls, message ) :
if cls.isLevel( Logger.WARN ) :
print "WARN : " + message

@classmethod
def error( cls, message ) :
if cls.isLevel( Logger.ERROR ) :
print "ERROR: " + message

@classmethod
def fatal( cls, message ) :
if cls.isLevel( Logger.FATAL ) :
print "FATAL: " + message


И некоторый код, который проверяет его немного:


def logAll() :
Logger.debug( "This is a Debug message." )
Logger.info ( "This is a Info message." )
Logger.warn ( "This is a Warn message." )
Logger.error( "This is a Error message." )
Logger.fatal( "This is a Fatal message." )

if __name__ == '__main__' :

print "Should see all DEBUG and higher"
Logger._level = Logger.DEBUG
logAll()

print "Should see all ERROR and higher"
Logger._level = Logger.ERROR
logAll()

ответил(а) 2010-05-11T04:48:00+04:00 10 лет, 5 месяцев назад
198

Альтернативные конструкторы являются классическим примером.

ответил(а) 2008-09-01T21:27:00+04:00 12 лет, 1 месяц назад
133

Я думаю, что самый ясный ответ - AmanKow. Это сводится к тому, как вы хотите организовать свой код. Вы можете написать все как функции уровня модуля, которые завернуты в пространство имен модуля i.e


module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass

usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass
def f5():pass

usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()


Вышеописанный процедурный код не является хорошо организованным, как вы можете видеть после того, как только 3 модуля запутались, что каждый метод делает? Вы можете использовать длинные описательные имена для функций (например, в java), но ваш код становится неуправляемым очень быстро.


Объектно-ориентированный способ состоит в том, чтобы разбивать ваш код на управляемые блоки. i.e Классы, объекты и функции могут быть связаны с экземплярами объектов или классами.


С помощью функций класса вы получаете еще один уровень деления в вашем коде по сравнению с функциями уровня модуля.
Таким образом, вы можете группировать связанные функции внутри класса, чтобы сделать их более конкретными для задачи, назначенной этому классу. Например, вы можете создать класс служебных файлов:


class FileUtil ():
def copy(source,dest):pass
def move(source,dest):pass
def copyDir(source,dest):pass
def moveDir(source,dest):pass

//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")


Этот способ более гибкий и удобен в обслуживании, вы объединяете функции вместе и более очевидны для каждой функции. Также вы предотвращаете конфликты имен, например, копия функции может существовать в другом импортированном модуле (например, сетевой копии), который вы используете в своем коде, поэтому, когда вы используете полное имя FileUtil.copy(), вы удаляете проблему и обе функции копирования могут использоваться бок о бок.

ответил(а) 2011-05-14T13:24:00+04:00 9 лет, 5 месяцев назад
125

Когда пользователь регистрируется на моем веб-сайте, объект User() создается из имени пользователя и пароля.


Если мне нужен пользовательский объект без входа пользователя (например, пользователь-администратор может захотеть удалить другую учетную запись пользователя, поэтому мне нужно создать экземпляр этого пользователя и вызвать его метод удаления):


У меня есть методы класса для захвата пользовательского объекта.


class User():
#lots of code
#...
# more code

@classmethod
def get_by_username(cls, username):
return cls.query(cls.username == username).get()

@classmethod
def get_by_auth_id(cls, auth_id):
return cls.query(cls.auth_id == auth_id).get()

ответил(а) 2012-03-26T09:36:00+04:00 8 лет, 7 месяцев назад
107

Он позволяет писать общие методы класса, которые можно использовать с любым совместимым классом.


Например:


@classmethod
def get_name(cls):
print cls.name

class C:
name = "tester"

C.get_name = get_name

#call it:
C.get_name()


Если вы не используете @classmethod, вы можете сделать это с помощью собственного ключевого слова, но ему нужен экземпляр класса:


def get_name(self):
print self.name

class C:
name = "tester"

C.get_name = get_name

#call it:
C().get_name() #<-note the its an instance of class C

ответил(а) 2012-12-26T18:11:00+04:00 7 лет, 10 месяцев назад
99

Я работал с PHP, и недавно я спрашивал себя, что происходит с этим классом? Руководство Python очень техничное и очень короткое в словах, поэтому оно не поможет с пониманием этой функции. Я был googling и googling, и я нашел ответ → http://code.anjanesh.net/2007/12/python-classmethods.html.


Если вам лениво нажимать на него. Мои объяснения короче и ниже.:)


в PHP (возможно, не все вы знаете PHP, но этот язык настолько прямолинейный, что все должны понимать, о чем я говорю), у нас есть такие статические переменные, как это:



class A
{

static protected $inner_var = null;

static public function echoInnerVar()
{
echo self::$inner_var."\n";
}

static public function setInnerVar($v)
{
self::$inner_var = $v;
}

}

class B extends A
{
}

A::setInnerVar(10);
B::setInnerVar(20);

A::echoInnerVar();
B::echoInnerVar();


Выход будет в обоих случаях 20.


Однако в python мы можем добавить декоратор @classmethod и, следовательно, можно получить выходные 10 и 20 соответственно. Пример:



class A(object):
inner_var = 0

@classmethod
def setInnerVar(cls, value):
cls.inner_var = value

@classmethod
def echoInnerVar(cls):
print cls.inner_var

class B(A):
pass

A.setInnerVar(10)
B.setInnerVar(20)

A.echoInnerVar()
B.echoInnerVar()


Умный, не?

ответил(а) 2010-02-22T16:27:00+03:00 10 лет, 8 месяцев назад
91

Методы класса предоставляют "семантический сахар" (не знаю, широко используется этот термин) - или "семантическое удобство".


Пример: у вас есть набор классов, представляющих объекты. Возможно, вам понадобится метод класса all() или find() для записи User.all() или User.find(firstname='Guido'). Это можно сделать, используя функции уровня модуля, конечно...

ответил(а) 2010-08-17T18:53:00+04:00 10 лет, 2 месяца назад
69

То, что только что ударило меня из Ruby, заключается в том, что так называемый метод класса и так называемый метод экземпляра - это просто функция с семантическим значением, применяемая к ее первому параметру, который молча передается, когда функция вызывается как метод объекта (т.е. obj.meth()).


Обычно этот объект должен быть экземпляром, но @classmethod decorator изменяет правила для прохождения класса. Вы можете вызвать метод класса в экземпляре (это просто функция) - первым элементом будет его класс.


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


Рассмотрим это:


class Foo():
def foo(x):
print(x)

Вы можете вызвать foo в экземпляре


Foo().foo()
<__main__.Foo instance at 0x7f4dd3e3bc20>

Но не для класса:


Foo.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)

Теперь добавьте @classmethod:


class Foo():
@classmethod
def foo(x):
print(x)

Вызов экземпляра теперь проходит его класс:


Foo().foo()
__main__.Foo

как и вызов класса:


Foo.foo()
__main__.Foo

Это единственное соглашение, которое диктует, что мы используем self для этого первого аргумента метода экземпляра и cls для метода класса. Я не использовал здесь, чтобы проиллюстрировать, что это просто аргумент. В Ruby self - это ключевое слово.


Контраст с Ruby:


class Foo
def foo()
puts "instance method #{self}"
end
def self.foo()
puts "class method #{self}"
end
end

Foo.foo()
class method Foo

Foo.new.foo()
instance method #<Foo:0x000000020fe018>


Метод класса Python - это просто украшенная функция, и вы можете использовать те же методы для создать свои собственные декораторы. Украшенный метод обертывает реальный метод (в случае @classmethod он передает дополнительный аргумент класса). Основной метод все еще существует, скрытый но все еще доступный.


footnote: Я написал это после столкновения имени между методом класса и экземпляра, вызвавшим мое любопытство. Я далек от эксперта Python и хотел бы комментариев, если это не так.

ответил(а) 2017-03-24T23:45:00+03:00 3 года, 7 месяцев назад
70

Это интересная тема. Я полагаю, что метод класса python работает как singleton, а не factory (который возвращает созданный экземпляр класса). Причина, по которой это синглтон, заключается в том, что существует общий объект, который создается (словарь), но только один раз для класса, но используется всеми экземплярами.


Чтобы проиллюстрировать это, вот пример. Обратите внимание, что все экземпляры имеют ссылку на один словарь. Это не шаблон factory, как я понимаю. Это, вероятно, очень уникально для python.


class M():
@classmethod
def m(cls, arg):
print "arg was", getattr(cls, "arg" , None),
cls.arg = arg
print "arg is" , cls.arg

M.m(1) # prints arg was None arg is 1
M.m(2) # prints arg was 1 arg is 2
m1 = M()
m2 = M()
m1.m(3) # prints arg was 2 arg is 3
m2.m(4) # prints arg was 3 arg is 4 << this breaks the factory pattern theory.
M.m(5) # prints arg was 4 arg is 5

ответил(а) 2012-09-07T08:06:00+04:00 8 лет, 1 месяц назад
58

Я задавал себе тот же вопрос несколько раз. И хотя ребята здесь очень старались это объяснить, ИМХО лучший ответ (и самый простой), который я нашел, это описание метода класса в документации на Python.


Существует также ссылка на метод Static. И если кто-то уже знает методы экземпляра (что я предполагаю), этот ответ может быть последней частью, чтобы собрать все это вместе...


Дальнейшую и более глубокую разработку этой темы можно найти и в документации:
Стандартная иерархия типов (прокрутите вниз до раздела Методы экземпляров)

ответил(а) 2014-10-14T10:07:00+04:00 6 лет назад
42

Класс определяет, конечно, набор экземпляров. И методы класса работают над отдельными экземплярами. Методы класса (и переменные) - место для зависания другой информации, связанной с множеством экземпляров по всем.


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


Вы также можете использовать методы класса для определения инструментов для работы со всем набором. Например, Student.all_of_em() может вернуть всех известных учеников. Очевидно, если ваш набор экземпляров имеет больше структуры, чем просто набор, вы можете предоставить методы класса, чтобы знать об этой структуре. Students.all_of_em (сорт = 'юниоры')


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

ответил(а) 2016-09-08T04:29:00+03:00 4 года, 1 месяц назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема