decorator trong lib tiêu chuẩn python (cụ thể là @deprecated)


127

Tôi cần đánh dấu các quy trình là không được dùng nữa, nhưng dường như không có trình trang trí thư viện chuẩn nào để không dùng nữa. Tôi biết các công thức cho nó và mô-đun cảnh báo, nhưng câu hỏi của tôi là: tại sao không có trình trang trí thư viện tiêu chuẩn cho tác vụ (phổ biến) này?

Câu hỏi bổ sung: có các trình trang trí tiêu chuẩn trong thư viện tiêu chuẩn không?


13
bây giờ có một gói không dùng nữa
muon

11
Tôi hiểu các cách để làm điều đó, nhưng đến đây để tìm hiểu thêm về lý do tại sao nó không có trong std lib (như tôi giả định là trường hợp của OP) và không thấy câu trả lời tốt cho câu hỏi thực tế
SwimBikeRun

4
Tại sao nó lại xảy ra quá thường xuyên khi các câu hỏi nhận được hàng tá câu trả lời mà thậm chí không cố gắng trả lời câu hỏi và chủ động bỏ qua những điều như "Tôi biết các công thức nấu ăn"? Thật đáng buồn!
Catskul

1
@Catskul vì điểm internet giả mạo.
Stefano Borini

1
Bạn có thể sử dụng Thư viện không được dùng nữa .
Laurent LAPORTE

Câu trả lời:


59

Đây là một số đoạn trích, được sửa đổi từ những đoạn trích được Leandro trích dẫn:

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

Bởi vì trong một số trình thông dịch, giải pháp đầu tiên được tiếp xúc (không xử lý bộ lọc) có thể dẫn đến cảnh báo bị loại bỏ.


14
Tại sao không sử dụng functools.wrapsthay vì đặt tên và tài liệu như vậy?
Maximilian

1
@Maximilian: Edited thêm rằng, để tiết kiệm tương lai sao chép pasters của mã này từ làm nó sai quá
Eric

17
Tôi không thích hiệu ứng phụ (bật / tắt bộ lọc). Công việc của người trang trí không phải là quyết định điều này.
Kentzo

1
Việc bật và tắt bộ lọc có thể kích hoạt bug.python.org/issue29672
gerrit

4
không trả lời câu hỏi thực tế.
Catskul

44

Đây là một giải pháp khác:

Trình trang trí này ( thực tế là một nhà máy trang trí ) cho phép bạn đưa ra thông báo lý do . Nó cũng hữu ích hơn khi giúp nhà phát triển chẩn đoán sự cố bằng cách cung cấp tên tệp nguồn và số dòng .

CHỈNH SỬA : Mã này sử dụng khuyến nghị của Zero: nó thay thế warnings.warn_explicitdòng bằng warnings.warn(msg, category=DeprecationWarning, stacklevel=2), in trang web gọi hàm thay vì trang định nghĩa hàm. Nó làm cho việc gỡ lỗi dễ dàng hơn.

EDIT2 : Phiên bản này cho phép nhà phát triển chỉ định một thông báo "lý do" tùy chọn.

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

Bạn có thể sử dụng trình trang trí này cho các hàm , phương thứclớp .

Đây là một ví dụ đơn giản:

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

Bạn sẽ nhận được:

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

EDIT3: Trình trang trí này hiện là một phần của thư viện không được chấp nhận:

Bản phát hành ổn định mới v1.2.10 🎉


6
Hoạt động tốt - Tôi thích thay thế warn_explicitdòng warnings.warn(msg, category=DeprecationWarning, stacklevel=2)in trang web gọi hàm hơn là trang định nghĩa hàm. Nó làm cho việc gỡ lỗi dễ dàng hơn.
không

Xin chào, tôi muốn sử dụng đoạn mã của bạn trong thư viện được cấp phép GPLv3 . Bạn có sẵn sàng cấp phép lại mã của mình theo GPLv3 hoặc bất kỳ giấy phép dễ dàng nào hơn , để tôi có thể làm như vậy một cách hợp pháp không?
gerrit


1
@LaurentLAPORTE Tôi biết. CC-BY-SO không cho phép sử dụng trong GPLv3 (vì bit chia sẻ giống nhau), đó là lý do tại sao tôi hỏi bạn có sẵn sàng phát hành mã này bổ sung cụ thể theo giấy phép tương thích với GPL không. Nếu không, điều đó tốt, và tôi sẽ không sử dụng mã của bạn.
gerrit, 09/07/2017

2
không trả lời câu hỏi thực tế.
Catskul

15

Như muon đề xuất , bạn có thể cài đặt deprecationgói này.

Các deprecationthư viện cung cấp một deprecatedtrang trí và fail_if_not_removedtrang trí cho các bài kiểm tra của bạn.

Cài đặt

pip install deprecation

Ví dụ sử dụng

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

Xem http://deprecation.readthedocs.io/ để có tài liệu đầy đủ.


4
không trả lời câu hỏi thực tế.
Catskul

1
Lưu ý PyCharm không nhận ra điều này
cz

12

Tôi đoán lý do là mã Python không thể được xử lý tĩnh (như nó được thực hiện cho các trình biên dịch C ++), bạn không thể nhận được cảnh báo về việc sử dụng một số thứ trước khi thực sự sử dụng nó. Tôi không nghĩ rằng bạn nên gửi spam cho người dùng tập lệnh của bạn bằng một loạt thông báo "Cảnh báo: nhà phát triển tập lệnh này đang sử dụng API không được dùng nữa".

Cập nhật: nhưng bạn có thể tạo trình trang trí sẽ biến đổi chức năng ban đầu thành chức năng khác. Chức năng mới sẽ đánh dấu / kiểm tra công tắc cho biết rằng chức năng này đã được gọi và sẽ chỉ hiển thị thông báo khi chuyển công tắc sang trạng thái bật. Và / hoặc khi thoát nó có thể in ra danh sách tất cả các hàm không dùng nữa được sử dụng trong chương trình.


3
Và bạn sẽ có thể cho biết không dùng nữa khi hàm được nhập từ mô-đun . Decorator sẽ là một công cụ phù hợp cho điều đó.
Janusz Lenar

@JanuszLenar, cảnh báo đó sẽ được hiển thị ngay cả khi chúng tôi không sử dụng hàm không dùng nữa. Nhưng tôi đoán tôi có thể cập nhật câu trả lời của mình với một số gợi ý.
ony

8

Bạn có thể tạo một tệp utils

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

Và sau đó nhập trình trang trí không dùng nữa như sau:

from .utils import deprecated

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

Cảm ơn, tôi đang sử dụng tính năng này để đưa người dùng đến đúng nơi thay vì chỉ hiển thị thông báo không dùng nữa!
Attanasio của Đức

3
không trả lời câu hỏi thực tế.
Catskul

2

CẬP NHẬT: Tôi nghĩ là tốt hơn, khi chúng tôi chỉ hiển thị DeprecationWarning lần đầu tiên cho mỗi dòng mã và khi chúng tôi có thể gửi một số thông báo:

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

Kết quả:

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

Chúng ta có thể chỉ cần nhấp vào đường dẫn cảnh báo và đi đến dòng trong PyCharm.


2
không trả lời câu hỏi thực tế.
Catskul

0

Làm tăng câu trả lời này bởi Steven Vascellaro :

Nếu bạn sử dụng Anaconda, trước tiên hãy cài đặt deprecationgói:

conda install -c conda-forge deprecation 

Sau đó dán phần sau lên đầu tệp

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

Xem http://deprecation.readthedocs.io/ để có tài liệu đầy đủ.


4
không trả lời câu hỏi thực tế.
Catskul
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.