Какова цель методов класса?
Я учу себя Python и мой последний урок был в том, что Python-это не Java, и поэтому я только что провел некоторое время, превращая все мои методы класса в функции.
теперь я понимаю, что мне не нужно использовать методы класса для того, что я бы сделал с static
методы в Java, но теперь я не уверен, когда я буду их использовать. Все советы, которые я могу найти о методах класса Python, относятся к новичкам, таким как я, и должны избегать их, а стандарт документация является наиболее непрозрачной при их обсуждении.
есть ли у кого-нибудь хороший пример использования метода класса в Python или, по крайней мере, может ли кто-нибудь сказать мне, когда методы класса могут быть разумно использованы?
15 ответов:
методы класса предназначены для тех случаев, когда вам нужно иметь методы, которые не являются специфичными для какого-либо конкретного экземпляра, но все же каким-то образом связаны с классом. Самое интересное в них то, что они могут быть переопределены подклассами, что просто невозможно в статических методах Java или функциях уровня модуля Python.
Если у вас есть класс
MyClass
, и функция уровня модуля, которая работает на MyClass (фабрика, заглушка инъекции зависимостей и т. д.), делает егоclassmethod
. Тогда он будет доступен для подклассов.
заводские методы (альтернативные конструкторы) действительно являются классическим примером методов класса.
в принципе, методы класса подходят в любое время, когда вы хотите иметь метод, который естественно вписывается в пространство имен класса, но не связан с конкретным экземпляром класса.
как пример, в отличном unipath модуль:
текущей директории
Path.cwd()
- вернуться фактический текущий каталог; например,
Path("/tmp/my_temp_dir")
. Это метод класса..chdir()
- сделать себя текущий каталог.
поскольку текущий каталог является широким процессом,
cwd
метод не имеет конкретного экземпляра, с которым он должен быть связан. Однако изменениеcwd
каталог данногоPath
экземпляр действительно должен быть методом экземпляра.Хммм... как
Path.cwd()
действительно возвращает aPath
например, я думаю, что это можно считать заводским методом...
подумайте об этом так: обычные методы полезны, чтобы скрыть детали отправки: вы можете ввести
myobj.foo()
, не беспокоясь о том, является лиfoo()
метод реализуется с помощью объект или один из его родительских классов. Методы класса точно аналогичны этому, но вместо этого с объектом класса: они позволяют вам вызыватьMyClass.foo()
, не беспокоясь о том,foo()
реализуется специальноMyClass
потому что он нуждался в своей собственной специализированной версии, или это так позволяя родительскому классу обрабатывать вызов.методы класса необходимы, когда вы делаете настройку или вычисление, что предваряет создание фактического экземпляра, потому что до тех пор, пока экземпляр не существует, вы, очевидно, не можете использовать экземпляр в качестве точки отправки для вызовов метода. Хороший пример можно посмотреть в исходном коде SQLAlchemy; взгляните на
dbapi()
метод класса при следующем ссылка:видно, что
dbapi()
метод, который серверная часть базы данных использует для импорта библиотеки базы данных конкретного поставщика, которая ему нужна по требованию, является методом класса, потому что ему нужно запустить до экземпляры определенного соединения с базой данных начинают создаваться - но это не может быть простой функцией или статическая функция, потому что они хотят, чтобы она могла вызывать другие, поддерживающие методы, которые также могут быть написаны более конкретно в подклассах, чем в их родительском классе. И если вы отправляете в функцию или статический класс, то вы "забываете" и теряете знание о том, какой класс выполняет инициализацию.
недавно я хотел очень легкий класс ведения журнала, который выводил бы различные объемы вывода в зависимости от уровня ведения журнала, который можно было бы программно установить. Но я не хотел создавать экземпляр класса каждый раз, когда я хотел вывести отладочное сообщение или ошибку или предупреждение. Но я также хотел инкапсулировать функционирование этого средства ведения журнала и сделать его многоразовым без объявления каких-либо глобалов.
поэтому я использовал переменные класса и
@classmethod
декоратор добиться этого.С помощью моего простого класса ведения журнала, я мог бы сделать следующее:
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." )
в "производственной" среде, я могу указать
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()
Я думаю, что самый ясный ответ AmanKow это один. Он сводится к тому, как вы хотите организовать свой код. Вы можете написать все как функции уровня модуля, которые завернуты в пространство имен модуля, т. е.
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), но все равно ваш код становится неуправляемым очень быстрый.
объектно-ориентированный способ состоит в том, чтобы разбить ваш код на управляемые блоки, т. е. классы и объекты и функции могут быть связаны с экземплярами объектов или с классами.
С помощью функций класса вы получаете другой уровень разделения в коде по сравнению с функциями уровня модуля. Таким образом, вы можете группировать связанные функции внутри класса, чтобы сделать их более специфичными для задачи, назначенной этому классу. Например, вы можете создать класс утилиты file :
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 () вы устраняете проблему, и обе функции копирования могут использоваться бок о бок.
когда пользователь входит на мой сайт, объект User() создается из имени пользователя и пароля.
Если мне нужен объект пользователя без пользователя, чтобы войти в систему (например, пользователь admin может захотеть удалить другую учетную запись пользователя, поэтому мне нужно создать экземпляр этого пользователя и вызвать его метод удаления):
У меня есть методы класса, чтобы захватить объект пользователя.
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()
Это позволяет писать универсальные методы класса, которые можно использовать с любым совместимым класс.
например:
@classmethod def get_name(cls): print cls.name class C: name = "tester" C.get_name = get_name #call it: C.get_name()
Если вы не используете
@classmethod
вы можете сделать это с помощью ключевого слова self, но ему нужен экземпляр класса: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
честно? Я никогда не находил применение для staticmethod или classmethod. Я еще не видел операции, которая не может быть выполнена с помощью глобальной функции или метода экземпляра.
было бы по-другому, если бы python использовал частные и защищенные члены больше, чем Java. В Java мне нужен статический метод, чтобы иметь доступ к закрытым членам экземпляра для выполнения каких-либо действий. В Python это редко бывает необходимо.
обычно я вижу людей, использующих staticmethods и classmethods, когда все они действительно должны сделать, это использовать модуль на уровне языка Python пространства имен лучше.
я раньше работал с PHP и недавно я спрашивал себя, что происходит с этим classmethod? Руководство Python очень техническое и очень короткое в словах, поэтому оно не поможет понять эту функцию. Я гуглил и гуглил, и я нашел ответ -> 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 decorator и, таким образом, можно иметь выход 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()
умный, да?
методы класса обеспечивают " семантический сахар "(не знаю, широко ли используется этот термин) - или"семантическое удобство".
пример: вы получили набор классов, представляющих объекты. Возможно, вы захотите иметь метод класса
all()
илиfind()
написатьUser.all()
илиUser.find(firstname='Guido')
. Конечно, это можно сделать с помощью функций уровня модуля...
Это интересная тема. Я считаю, что python classmethod работает как синглтон, а не фабрика (которая возвращает созданный экземпляр класса). Причина, по которой он является синглтоном, заключается в том, что существует общий объект, который создается (словарь), но только один раз для класса, но совместно используется всеми экземплярами.
чтобы проиллюстрировать это вот пример. Обратите внимание, что все экземпляры имеют ссылку на один словарь. Это не заводская модель, как я понять это. Это, вероятно, очень уникально для 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
что только что поразило меня, исходя из Руби, это так называемый класс метод и так называемый экземпляр метод-это просто функция с семантическим значением, примененным к ее первому параметру, который молча передается, когда функции вызывается как метод объект (т. е.
obj.meth()
).обычно объект должен быть экземпляр, но
@classmethod
способ декоратор изменение правила для прохождения класса. Вы можете вызвать метод класса на экземпляре (это просто функция) - первый argyment будет его классом.потому что это просто функции, он может быть объявлен только один раз в любой заданной области (т. е.
class
определению). Если, следовательно, Следовательно, в качестве сюрприза рубист, что вы не можете иметь метод класса и метод экземпляра с тем же именем.рассмотрим следующий пример:
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
это ключевое слово.контраст с Рубином:
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
он передает дополнительный аргумент класса). Основной метод все еще там, скрытый можно найти.
сноску: Я написал это после того, как столкновение имен между классом и методом экземпляра вызвало мое любопытство. Я далек от эксперта по Python и хотел бы комментарии, если ошибаюсь.
Я задаю себе тот же вопрос несколько раз. И хотя ребята здесь старались объяснить это, ИМХО лучший ответ (и самый простой) ответ, который я нашел, это описание метода класса в документации Python.
существует также ссылка на статический метод. И в случае, если кто-то уже знает методы экземпляра (которые я предполагаю), этот ответ может быть последней частью, чтобы собрать все это вместе...
дальнейшая и более глубокая разработка с этой темой можно ознакомиться также в документации: стандартная иерархия типа (прокрутите вниз, чтобы методы экземпляра)
класс определяет набор экземпляров, конечно. И методы класса работают на отдельных экземплярах. Методы класса (и переменные) место, чтобы повесить другую информацию, которая связана с набором экземпляров по всем.
например, если ваш класс определяет набор учащихся, вам могут понадобиться переменные класса или методы, которые определяют такие вещи, как набор классов, членами которых могут быть учащиеся.
вы также можете использовать методы класса для определения инструментов для работаем над всем набором. Например, студент.all_of_em () может вернуть всех известных студентов. Очевидно, что если ваш набор экземпляров имеет больше структуры, чем просто набор, Вы можете предоставить методы класса, чтобы узнать об этой структуре. Студенты.all_of_em (grade='юниоры')
такие методы, как правило, приводят к хранению членов набора экземпляров в структурах данных, которые коренятся в переменных класса. Вам нужно позаботиться о том, чтобы не расстраивать сборку мусора.