python standart kitaplığındaki dekoratörler (özellikle @deprecated)


128

Rutinleri kullanımdan kaldırıldı olarak işaretlemem gerekiyor, ancak görünüşe göre kullanımdan kaldırılacak standart bir kitaplık dekoratörü yok. Bunun için tariflerin ve uyarılar modülünün farkındayım, ancak sorum şu: Bu (ortak) görev için neden standart bir kütüphane dekoratörü yok?

Ek soru: standart kitaplıkta standart dekoratörler var mı?


13
şimdi bir kullanımdan kaldırma paketi var
muon

11
Bunu yapmanın yollarını anlıyorum, ancak neden std lib'de bulunmadığına dair biraz fikir edinmek için buraya geldim (OP örneğinde olduğu gibi) ve asıl soruya iyi bir cevap göremiyorum
SwimBikeRun

4
Neden bu kadar sık ​​oluyor ki sorular, soruyu yanıtlamaya bile kalkışmayan düzinelerce yanıt alıyor ve "Tariflerin farkındayım" gibi şeyleri aktif olarak görmezden geliyor? Çıldırtıcı!
Catskul

1
Sahte internet noktaları nedeniyle @Catskul.
Stefano Borini

1
Kullanımdan Kaldırılmış Kitaplığı kullanabilirsiniz .
Laurent LAPORTE

Yanıtlar:


59

İşte Leandro tarafından alıntı yapılanlardan değiştirilen bazı pasajlar:

import warnings
import functools

def deprecated(func):
    """This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used."""
    @functools.wraps(func)
    def new_func(*args, **kwargs):
        warnings.simplefilter('always', DeprecationWarning)  # turn off filter
        warnings.warn("Call to deprecated function {}.".format(func.__name__),
                      category=DeprecationWarning,
                      stacklevel=2)
        warnings.simplefilter('default', DeprecationWarning)  # reset filter
        return func(*args, **kwargs)
    return new_func

# Examples

@deprecated
def some_old_function(x, y):
    return x + y

class SomeClass:
    @deprecated
    def some_old_method(self, x, y):
        return x + y

Çünkü bazı tercümanlarda ortaya çıkan ilk çözüm (filtre kullanılmadan) bir uyarı bastırmasına neden olabilir.


14
functools.wrapsAdı ve dokümanı böyle ayarlamak yerine neden kullanmayasınız ?
Maximilian

1
@Maximilian: Gelecekteki bu kodu kopyalayıp yapıştırıcıların da yanlış yapmasını önlemek için bunu eklemek için düzenlendi
Eric

17
Yan etkiyi sevmiyorum (filtreyi açma / kapama). Buna karar vermek dekoratörün işi değil.
Kentzo

1
Filtreyi açmak ve kapatmak bugs.python.org/issue29672'yi
gerrit

4
asıl soruya cevap vermiyor.
Catskul

45

İşte başka bir çözüm:

Bu dekoratör ( aslında bir dekoratör fabrikası ) bir neden mesajı vermenize izin verir . Ayrıca, geliştiricinin sorunu kaynak dosya adını ve satır numarasını vererek teşhis etmesine yardımcı olmak daha kullanışlıdır .

DÜZENLEME : Bu kod, Zero'nun önerisini kullanır: işlev tanımlama sitesi yerine işlev çağrı sitesini yazdıran warnings.warn_explicitsatırı ile değiştirir warnings.warn(msg, category=DeprecationWarning, stacklevel=2). Hata ayıklamayı kolaylaştırır.

DÜZENLEME2 : Bu sürüm, geliştiricinin isteğe bağlı bir "neden" mesajı belirtmesine izin verir.

import functools
import inspect
import warnings

string_types = (type(b''), type(u''))


def deprecated(reason):
    """
    This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used.
    """

    if isinstance(reason, string_types):

        # The @deprecated is used with a 'reason'.
        #
        # .. code-block:: python
        #
        #    @deprecated("please, use another function")
        #    def old_function(x, y):
        #      pass

        def decorator(func1):

            if inspect.isclass(func1):
                fmt1 = "Call to deprecated class {name} ({reason})."
            else:
                fmt1 = "Call to deprecated function {name} ({reason})."

            @functools.wraps(func1)
            def new_func1(*args, **kwargs):
                warnings.simplefilter('always', DeprecationWarning)
                warnings.warn(
                    fmt1.format(name=func1.__name__, reason=reason),
                    category=DeprecationWarning,
                    stacklevel=2
                )
                warnings.simplefilter('default', DeprecationWarning)
                return func1(*args, **kwargs)

            return new_func1

        return decorator

    elif inspect.isclass(reason) or inspect.isfunction(reason):

        # The @deprecated is used without any 'reason'.
        #
        # .. code-block:: python
        #
        #    @deprecated
        #    def old_function(x, y):
        #      pass

        func2 = reason

        if inspect.isclass(func2):
            fmt2 = "Call to deprecated class {name}."
        else:
            fmt2 = "Call to deprecated function {name}."

        @functools.wraps(func2)
        def new_func2(*args, **kwargs):
            warnings.simplefilter('always', DeprecationWarning)
            warnings.warn(
                fmt2.format(name=func2.__name__),
                category=DeprecationWarning,
                stacklevel=2
            )
            warnings.simplefilter('default', DeprecationWarning)
            return func2(*args, **kwargs)

        return new_func2

    else:
        raise TypeError(repr(type(reason)))

Bu dekoratörü işlevler , yöntemler ve sınıflar için kullanabilirsiniz .

İşte basit bir örnek:

@deprecated("use another function")
def some_old_function(x, y):
    return x + y


class SomeClass(object):
    @deprecated("use another method")
    def some_old_method(self, x, y):
        return x + y


@deprecated("use another class")
class SomeOldClass(object):
    pass


some_old_function(5, 3)
SomeClass().some_old_method(8, 9)
SomeOldClass()

Alacaksınız:

deprecated_example.py:59: DeprecationWarning: Call to deprecated function or method some_old_function (use another function).
  some_old_function(5, 3)
deprecated_example.py:60: DeprecationWarning: Call to deprecated function or method some_old_method (use another method).
  SomeClass().some_old_method(8, 9)
deprecated_example.py:61: DeprecationWarning: Call to deprecated class SomeOldClass (use another class).
  SomeOldClass()

DÜZENLEME3: Bu dekoratör artık Kullanımdan Kaldırılan kitaplığın bir parçasıdır:

Yeni kararlı sürüm v1.2.10 🎉


6
İşe yarıyor - İşlev tanımlama sitesi yerine işlev çağrısı sitesini yazdıran warn_explicitsatırı değiştirmeyi tercih ederim warnings.warn(msg, category=DeprecationWarning, stacklevel=2). Hata ayıklamayı kolaylaştırır.
Zero

Merhaba, kod pasajınızı GPLv3 lisanslı bir kitaplıkta kullanmak istiyorum . Yasal olarak bunu yapabilmem için kodunuzu GPLv3 veya daha fazla izin verilen lisans kapsamında yeniden lisanslamak ister misiniz ?
gerrit


1
@LaurentLAPORTE Biliyorum. CC-BY-SO, GPLv3 içinde kullanıma izin vermez (benzer paylaşma biti nedeniyle), bu nedenle, bu kodu özellikle ek olarak GPL uyumlu bir lisans altında yayınlamak isteyip istemediğinizi soruyorum. Değilse, sorun değil ve kodunu kullanmayacağım.
gerrit

2
asıl soruya cevap vermiyor.
Catskul

15

Muon'un önerdiği gibi , bunun için deprecationpaketi kurabilirsiniz .

deprecationKütüphanesi sağlar deprecateddekoratör ve fail_if_not_removedsizin testler için dekoratör.

Kurulum

pip install deprecation

Örnek Kullanım

import deprecation

@deprecation.deprecated(deprecated_in="1.0", removed_in="2.0",
                        current_version=__version__,
                        details="Use the bar function instead")
def foo():
    """Do some stuff"""
    return 1

Tüm belgeler için http://deprecation.readthedocs.io/ adresine bakın .


4
asıl soruya cevap vermiyor.
Catskul

1
Not pycharm tanımıyor
cz

12

Sanırım nedeni Python kodunun statik olarak işlenememesi (C ++ derleyicileri için olduğu gibi), gerçekten kullanmadan önce bazı şeyleri kullanma konusunda uyarı alamazsınız. Komut dosyanızın kullanıcısına bir grup mesajla "Uyarı: bu komut dosyasının geliştiricisi, kullanımdan kaldırılmış API kullanıyor" mesajıyla spam yapmanın iyi bir fikir olduğunu düşünmüyorum.

Güncelleme: ancak orijinal işlevi bir başkasına dönüştürecek bir dekoratör oluşturabilirsiniz. Yeni işlev, bu işlevin zaten çağrıldığını belirten anahtarı işaretleyecek / kontrol edecek ve yalnızca anahtarı açık duruma getirdiğinizde mesaj gösterecektir. Ve / veya çıkışta, programda kullanılan tüm kullanımdan kaldırılmış işlevlerin listesini yazdırabilir.


3
Ve işlev modülden içe aktarıldığında kullanımdan kaldırmayı belirtebilmelisiniz . Dekoratör bunun için doğru bir araç olacaktır.
Janusz Lenar

@JanuszLenar, bu kullanımdan kaldırılmış işlevi kullanmasak bile bu uyarı gösterilecek. Ama sanırım cevabımı bir ipucu ile güncelleyebilirim.
ony

8

Bir utils dosyası oluşturabilirsiniz

import warnings

def deprecated(message):
  def deprecated_decorator(func):
      def deprecated_func(*args, **kwargs):
          warnings.warn("{} is a deprecated function. {}".format(func.__name__, message),
                        category=DeprecationWarning,
                        stacklevel=2)
          warnings.simplefilter('default', DeprecationWarning)
          return func(*args, **kwargs)
      return deprecated_func
  return deprecated_decorator

Ve sonra kullanımdan kaldırma dekoratörünü aşağıdaki gibi içe aktarın:

from .utils import deprecated

@deprecated("Use method yyy instead")
def some_method()"
 pass

Teşekkürler, bunu sadece kullanımdan kaldırma mesajını göstermek yerine kullanıcıyı doğru yere göndermek için kullanıyorum!
German Attanasio

3
asıl soruya cevap vermiyor.
Catskul

2

GÜNCELLEME: Her kod satırı için DeprecationWarning'i yalnızca ilk kez gösterdiğimizde ve bir mesaj gönderebileceğimizde daha iyi olduğunu düşünüyorum :

import inspect
import traceback
import warnings
import functools

import time


def deprecated(message: str = ''):
    """
    This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used first time and filter is set for show DeprecationWarning.
    """
    def decorator_wrapper(func):
        @functools.wraps(func)
        def function_wrapper(*args, **kwargs):
            current_call_source = '|'.join(traceback.format_stack(inspect.currentframe()))
            if current_call_source not in function_wrapper.last_call_source:
                warnings.warn("Function {} is now deprecated! {}".format(func.__name__, message),
                              category=DeprecationWarning, stacklevel=2)
                function_wrapper.last_call_source.add(current_call_source)

            return func(*args, **kwargs)

        function_wrapper.last_call_source = set()

        return function_wrapper
    return decorator_wrapper


@deprecated('You must use my_func2!')
def my_func():
    time.sleep(.1)
    print('aaa')
    time.sleep(.1)


def my_func2():
    print('bbb')


warnings.simplefilter('always', DeprecationWarning)  # turn off filter
print('before cycle')
for i in range(5):
    my_func()
print('after cycle')
my_func()
my_func()
my_func()

Sonuç:

before cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:45: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
aaa
aaa
aaa
aaa
after cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:47: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:48: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:49: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa

Process finished with exit code 0

PyCharm'daki uyarı yoluna tıklayıp satıra gidebiliriz.


2
asıl soruya cevap vermiyor.
Catskul

0

Augmenting Steven Vascellaro tarafından bu cevabı :

Anaconda kullanıyorsanız, önce deprecationpaketi kurun :

conda install -c conda-forge deprecation 

Ardından aşağıdakini dosyanın üstüne yapıştırın

import deprecation

@deprecation.deprecated(deprecated_in="1.0", removed_in="2.0",
                    current_version=__version__,
                    details="Use the bar function instead")
def foo():
    """Do some stuff"""
    return 1

Tüm belgeler için http://deprecation.readthedocs.io/ adresine bakın .


4
asıl soruya cevap vermiyor.
Catskul
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.