Какова цель методов класса?

(добавление ответа, потому что примеров с универсальными символами Generics недостаточно)

       // Source 
       List<Integer> intList = Arrays.asList(1,2,3);
       List<Double> doubleList = Arrays.asList(2.78,3.14);
       List<Number> numList = Arrays.asList(1,2,2.78,3.14,5);

       // Destination
       List<Integer> intList2 = new ArrayList<>();
       List<Double> doublesList2 = new ArrayList<>();
       List<Number> numList2 = new ArrayList<>();

        // Works
        copyElements1(intList,intList2);         // from int to int
        copyElements1(doubleList,doublesList2);  // from double to double


     static <T> void copyElements1(Collection<T> src, Collection<T> dest) {
        for(T n : src){
            dest.add(n);
         }
      }


     // Let's try to copy intList to its supertype
     copyElements1(intList,numList2); // error, method signature just says "T"
                                      // and here the compiler is given 
                                      // two types: Integer and Number, 
                                      // so which one shall it be?

     // PECS to the rescue!
     copyElements2(intList,numList2);  // possible



    // copy Integer (? extends T) to its supertype (Number is super of Integer)
    private static <T> void copyElements2(Collection<? extends T> src, 
                                          Collection<? super T> dest) {
        for(T n : src){
            dest.add(n);
        }
    }
244
задан martineau 26 September 2018 в 09:34
поделиться

9 ответов

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

, Если у Вас есть класс MyClass, и функция уровня модуля, которая воздействует на MyClass (фабрика, тупик внедрения зависимости, и т.д.), делает его classmethod. Тогда это будет доступно подклассам.

173
ответ дан John Millikin 23 November 2019 в 03:09
поделиться

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

25
ответ дан Aaron Maenpaa 23 November 2019 в 03:09
поделиться

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

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

Как пример, в превосходном модуль unipath :

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

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

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

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

62
ответ дан user114245 23 November 2019 в 03:09
поделиться

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

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

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

6
ответ дан Jason Baker 23 November 2019 в 03:09
поделиться

@classmethod может быть полезно для того, чтобы легко инстанцировать объектов того класса от внешних ресурсов. Рассмотрите следующее:

import settings

class SomeClass:
    @classmethod
    def from_settings(cls):
        return cls(settings=settings)

    def __init__(self, settings=None):
        if settings is not None:
            self.x = settings['x']
            self.y = settings['y']

Затем в другом файле:

from some_package import SomeClass

inst = SomeClass.from_settings()

Доступ inst.x даст то же значение как настройки ['x'].

0
ответ дан 23 November 2019 в 03:09
поделиться

Раньше я работал с PHP и недавно я спросил себя, что происходит с этим методом? Руководство по 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 и таким образом можно получить на выходе 10 и 20 соответственно. Example:


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()

Smart, ain't?

5
ответ дан 23 November 2019 в 03:09
поделиться

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

Поэтому я использовал переменные класса и декоратор @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." )

В "производственной" среде я могу указать

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()
27
ответ дан 23 November 2019 в 03:09
поделиться

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

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

4
ответ дан 23 November 2019 в 03:09
поделиться

Подумайте об этом так: обычные методы полезны, чтобы скрыть детали диспетчеризации: вы можете набрать 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(), который бэкенд базы данных использует для импорта необходимой ему библиотеки базы данных конкретного производителя по требованию, является методом класса, потому что он должен выполняться до того, как начнут создаваться экземпляры конкретного соединения с базой данных - но он не может быть простой функцией или статической функцией, потому что они хотят, чтобы он мог вызывать другие, вспомогательные методы, которые могут быть написаны более конкретно в подклассах, чем в их родительском классе. А если вы диспетчеризируете функцию или статический класс, то вы "забываете" и теряете знание о том, какой класс выполняет инициализацию.

46
ответ дан 23 November 2019 в 03:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: