Thông báo "Quá ít phương pháp công khai" của pylint có nghĩa là gì


110

Tôi đang chạy pylint trên một số mã và nhận được lỗi "Quá ít phương thức công khai (0/2)". Thông điệp này có nghĩa là gì? Tài liệu pylint không hữu ích:

Được sử dụng khi lớp có quá ít phương thức công khai, vì vậy hãy chắc chắn rằng nó thực sự đáng giá.


1
Lớp học của bạn trông như thế nào? Lớp có làm gì khác ngoài lưu trữ dữ liệu không?
Máy xay sinh tố

1
Tất cả những gì lớp làm là lưu trữ dữ liệu.
ông chủ

2
Vâng, có vấn đề của bạn. Các lớp học không phải để lưu trữ dữ liệu. Đó là những gì cấu trúc dữ liệu như từ điển và danh sách.
Máy xay sinh tố

Thú vị, cảm ơn! Thông báo lỗi pylint có thể hữu ích hơn. Dù sao, hãy chuyển bình luận của bạn thành một câu trả lời và tôi sẽ chấp thuận.
ông chủ

6
Nhưng đâu là định nghĩa của "vài"? Tôi có chính xác một phương pháp. Đó là lý do mà lớp học tồn tại. Làm thế nào để pylint định nghĩa "vài"? Nhiều hơn 2? Tại sao?
Zordid

Câu trả lời:


124

Về cơ bản, lỗi nói rằng các lớp không có nghĩa là chỉ lưu trữ dữ liệu, vì về cơ bản bạn đang coi lớp như một từ điển. Các lớp phải có ít nhất một vài phương thức để hoạt động trên dữ liệu mà chúng nắm giữ.

Nếu lớp của bạn trông như thế này:

class MyClass(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

Cân nhắc sử dụng từ điển hoặc namedtuplethay thế. Mặc dù nếu một lớp có vẻ là lựa chọn tốt nhất, hãy sử dụng nó. pylint không phải lúc nào cũng biết điều gì tốt nhất.

Xin lưu ý rằng điều đó namedtuplelà bất biến và các giá trị được chỉ định trên khởi tạo không thể được sửa đổi sau đó.


72
+1 cho "pylint không biết điều gì tốt nhất" - sử dụng phán đoán của riêng bạn nhưng theo quy tắc, nếu thứ bạn cần là "struct", hãy sử dụng dicthoặc namedtuple. Sử dụng một lớp khi bạn muốn thêm một số logic vào đối tượng của mình (ví dụ: bạn muốn mọi thứ xảy ra khi nó được tạo, bạn cần một số điều đặc biệt xảy ra khi nó được thêm vào, bạn muốn thực hiện một số thao tác trên nó, kiểm soát cách thức của nó. hiển thị, v.v.)
Burhan Khalid

Cảm ơn vì những câu trả lời chi tiết! Trường hợp sử dụng của tôi tương tự như những gì Burhan đã đề cập, tôi đang thực hiện một số xử lý trên dữ liệu khi nó được tạo.
thưa ông

6
Lỗi này không có ý nghĩa nếu bạn có Meta (siêu kính) bên trong định nghĩa lớp của bạn.
alexander_ch

11
namedtupletệ - ngoài việc có cú pháp xấu, bạn không thể ghi lại nó hoặc cung cấp các giá trị mặc định một cách dễ dàng.
rr-

6
Mỗi lần sử dụng namedtupletôi đều hối hận vì quyết định đó. Không nhất quán khi cho phép cả quyền truy cập được đặt tên và thuộc tính truy cập được lập chỉ mục.
thuyết

39

Nếu bạn đang mở rộng một lớp, thì đề xuất của tôi là vô hiệu hóa cảnh báo này một cách có hệ thống và tiếp tục, ví dụ: trong trường hợp tác vụ Cần tây:

class MyTask(celery.Task):  # pylint: disable=too-few-public-methods                                                                                   
    """base for My Celery tasks with common behaviors; extends celery.Task

    ...             

Ngay cả khi bạn chỉ mở rộng một hàm duy nhất, bạn chắc chắn cần một lớp để làm cho kỹ thuật này hoạt động và việc mở rộng chắc chắn tốt hơn hack trên các lớp của bên thứ ba!


Có thiểu năng này, trước cam kết hiện nay mang lại cho tôi: giá trị tùy chọn Bad 'quá vài công-phương pháp' (xấu-option-value)
Thủy

Bạn đã bao gồm các phương thức 's'? Tin nhắn bad-option-value của bạn không có nó.
sage,

4
Có lẽ một cách tốt hơn để vô hiệu hóa điều này là đặt min-public-methods=0trong [BASIC]phần của tệp cấu hình. Điều này cho phép bạn đặt nó trên một dòng riêng biệt với tất cả disable=nội dung của bạn (trong [MESSAGE CONTROL]) mà tôi thấy giúp dễ dàng hơn khi thêm các nhận xét chi tiết về lý do bạn bật và tắt mọi thứ cùng với thay đổi cấu hình.
cjs

15

Đây là một trường hợp khác của pylintcác quy tắc mù quáng.

"Các lớp không dùng để lưu trữ dữ liệu" - đây là một tuyên bố sai. Từ điển không tốt cho mọi thứ. Thành viên dữ liệu của một lớp là một cái gì đó có ý nghĩa, một mục từ điển là một cái gì đó tùy chọn. Bằng chứng: bạn có thể làm dictionary.get('key', DEFAULT_VALUE)để ngăn chặn a KeyError, nhưng không đơn giản __getattr__với mặc định.

EDIT - các cách được đề xuất để sử dụng cấu trúc

Tôi cần cập nhật câu trả lời của mình. Ngay bây giờ - nếu bạn cần struct, bạn có hai lựa chọn tuyệt vời:

a) Chỉ cần sử dụng attrs

Đây là một thư viện cho điều đó:

https://www.attrs.org/en/stable/

import attr

@attr.s
class MyClass(object):  # or just MyClass: for Python 3
    foo = attr.ib()
    bar = attr.ib()

Những gì bạn nhận được thêm: không phải viết hàm tạo, giá trị mặc định, xác thực, __repr__đối tượng chỉ đọc (để thay thế namedtuples, ngay cả trong Python 2) và hơn thế nữa.

b) Sử dụng dataclasses(Py 3.7+)

Sau nhận xét của hwjp, tôi cũng khuyên bạn nên dataclasses:

https://docs.python.org/3/library/dataclasses.html

Điều này gần như tốt attrsvà là một cơ chế thư viện tiêu chuẩn ("bao gồm pin"), không có phụ thuộc bổ sung, ngoại trừ Python 3.7+.

Phần còn lại của câu trả lời trước

NamedTuplekhông phải là tuyệt vời - đặc biệt là trước python 3's typing.NamedTuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple - bạn chắc chắn nên xem mẫu "lớp bắt nguồn từ NamedTuple". Python 2 - namedtuplesđược tạo ra từ các mô tả chuỗi - xấu, tệ và "lập trình bên trong chuỗi ký tự" ngu ngốc.

Tôi đồng ý với hai câu trả lời hiện tại ("cân nhắc sử dụng thứ khác, nhưng pylint không phải lúc nào cũng đúng" - câu được chấp nhận và "sử dụng bình luận ngăn chặn pylint"), nhưng tôi có đề xuất của riêng mình.

Hãy để tôi chỉ ra điều này một lần nữa: Một số lớp chỉ dùng để lưu trữ dữ liệu.

Bây giờ tùy chọn cũng cần xem xét - sử dụng property-ies.

class MyClass(object):
    def __init__(self, foo, bar):
        self._foo = foo
        self._bar = bar

    @property
    def foo(self):
        return self._foo

    @property
    def bar(self):
        return self._bar

Ở trên, bạn có các thuộc tính chỉ đọc, điều này được chấp nhận cho Đối tượng giá trị (ví dụ như các thuộc tính trong Thiết kế theo hướng miền), nhưng bạn cũng có thể cung cấp các bộ định vị - bằng cách này, lớp của bạn sẽ có thể chịu trách nhiệm cho các trường mà bạn có - ví dụ: để thực hiện một số xác thực, v.v. (nếu bạn có các bộ thiết lập, bạn có thể chỉ định bằng cách sử dụng chúng trong phương thức khởi tạo, tức là self.foo = foothay vì trực tiếp self._foo = foo, nhưng cẩn thận, các bộ thiết lập có thể giả định các trường khác đã được khởi tạo và sau đó bạn cần xác thực tùy chỉnh trong phương thức khởi tạo) .


2
Trong Python 3.7 trở lên, dataclasses cung cấp một giải pháp tốt, giải quyết một số điểm xấu của các cặp có tên và chúng hoàn hảo cho các Đối tượng Giá trị DDD.
hwjp

Tôi đồng ý và từ năm 2020 trở đi đó là con đường tiêu chuẩn. Để có cơ chế phạm vi phiên bản rộng (2.7, 3.3+ nếu tôi nhớ), bạn có thể sử dụng attrsthư viện, thực ra là bản thiết kế để tạo dataclassesmô-đun.
Tomasz Gandor

namedtuplescó cú pháp kỳ lạ để kế thừa ... yêu cầu mọi lớp sử dụng một lớp phải biết rằng đó là một tuple được đặt tên và sử dụng __new__thay vì __init__. dataclasseskhông có hạn chế này
Erik Aronesty

4

Thật khó khi sếp của bạn mong đợi nguyên tắc trách nhiệm duy nhất, nhưng pylint nói không. Vì vậy, hãy thêm phương thức thứ hai vào lớp của bạn để lớp của bạn vi phạm nguyên tắc trách nhiệm duy nhất. Bạn có ý định thực hiện nguyên tắc trách nhiệm đơn lẻ đến đâu là trong mắt người xem.

Sửa chữa của tôi,

Tôi đã thêm một phương thức bổ sung vào lớp của mình, vì vậy bây giờ nó thực hiện được 2 việc.

def __str__(self):
    return self.__class__.__name__

Tôi chỉ tự hỏi liệu bây giờ tôi có cần chia lớp học của mình thành 2 tệp riêng biệt hay không và có thể là cả các mô-đun.

vấn đề đã được giải quyết, nhưng không phải với các đồng nghiệp của tôi, những người dành cả ngày để tranh luận về thông số kỹ thuật, thay vì tiếp tục với nó, giống như đó là sự sống và cái chết.

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.