【问题标题】:What does Pylint's "Too few public methods" message mean?Pylint 的“公共方法太少”消息是什么意思?
【发布时间】:2012-12-11 05:37:15
【问题描述】:

我在某些代码上运行 Pylint,并收到错误“公共方法太少 (0/2)”。这条消息是什么意思?

Pylint documentation 没有帮助:

当一个类的公共方法太少时使用,所以一定要确保它真的值得。

【问题讨论】:

  • 你的班级是什么样的?除了存储数据之外,该类还做其他事情吗?
  • 该类所做的只是存储数据。
  • 好吧,这就是你的问题。类并不意味着存储数据。这就是字典和列表等数据结构的用途。
  • 有趣,谢谢! pylint 错误消息可能会更有用。无论如何,请随时将您的评论变成答案,我会批准。
  • 但是“很少”的定义在哪里?我只有一种方法。这就是类存在的原因。 pylint 如何定义“少数”?超过2个?为什么?

标签: python pylint


【解决方案1】:

该错误基本上表明类并不意味着只是存储数据,因为您基本上将类视为字典。类应该至少有几个方法来操作它们所持有的数据。

如果你的班级是这样的:

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

考虑改用字典或namedtuple。虽然如果一个类看起来是最好的选择,那就使用它。 Pylint 并不总是知道什么是最好的。

请注意namedtuple 是不可变的,实例化时分配的值以后不能修改。

【讨论】:

  • +1 表示“pylint 不知道什么是最好的” - 使用您自己的判断,但通常,如果您需要的是“结构”,请使用 dictnamedtuple。当你想为你的对象添加一些逻辑时使用一个类(例如,你希望在创建它时发生一些事情,你需要在添加它时发生一些特殊的事情,你想要对它执行一些操作,控制它如何显示等)
  • 如果您的类定义中有 Meta(元类),则此错误没有意义。
  • namedtuple 糟透了——除了语法丑陋之外,您还不能轻松记录它或提供默认值。
  • 每次我使用namedtuple 都会后悔这个决定。允许命名访问和索引访问属性是不一致的。
【解决方案2】:

如果您正在扩展一个类,那么我的建议是系统地禁用此警告并继续前进,例如,在 Celery 任务的情况下:

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

    ...             

即使你只是扩展一个功能,你肯定需要一个类来让这个技术发挥作用,而且扩展肯定比在第三方类上破解更好!

【讨论】:

  • 有了这个 diable,pre-commit 现在给了我:Bad option value 'too-few-public-method' (bad-option-value)
  • 您是否在方法中包含了“s”?您的 bad-option-value 消息没有它。
  • 禁用此功能的更好方法可能是在配置文件的[BASIC] 部分设置min-public-methods=0。这使您可以将其与所有 disable= 内容(在 [MESSAGE CONTROL] 中)放在单独的行上,我发现这样可以更轻松地添加有关您启用和禁用内容的原因以及配置更改的详细 cmet。
【解决方案3】:

这是皮林特盲法的另一个例子。

“类不是用来存储数据的”——这是一个错误的陈述。字典并不适合所有事情。类的数据成员是有意义的,字典项是可选的。证明:你可以做dictionary.get('key', DEFAULT_VALUE)来防止KeyError,但没有简单的__getattr__默认。

推荐的结构体使用方法

我需要更新我的答案。现在 - 如果您需要 struct,您有两个不错的选择:

a) 只需使用attrs

这是一个库:

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

你会得到额外的东西:不编写构造函数、默认值、验证、__repr__、只读对象(替换 namedtuples,即使在 Python 2 中也是如此)等等。

b) 使用dataclasses (Py 3.7+)

根据hwjp的评论,我也推荐dataclasses

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

这几乎和attrs 一样好,并且是标准库机制(“包括电池”),没有额外的依赖项,除了 Python 3.7+。

上一个答案的其余部分

NamedTuple 不是很好——尤其是在 Python 3 的typing.NamedTuple 之前: https://docs.python.org/3/library/typing.html#typing.NamedTuple

  • 您绝对应该查看“派生自NamedTuple 的类”模式。 Python 2 - namedtuples 从字符串描述创建 - 丑陋、糟糕并且“在字符串文字中编程”很愚蠢。

我同意当前的两个答案(“考虑使用其他东西,但 Pylint 并不总是正确的” - 已接受的答案和“使用 Pylint 抑制评论”),但我有自己的建议。

让我再次指出这一点:有些类只是用于存储数据。

现在也可以选择考虑 - 使用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

上面你有只读属性,这对于值对象是可以的(例如,像领域驱动设计中的那些),但你也可以提供设置器 - 这样你的类将能够对你的字段负责有 - 例如做一些验证等(如果你有设置器,你可以在构造函数中使用它们进行分配,即self.foo = foo而不是直接self._foo = foo,但要小心,设置器可能假设其他字段已经初始化,然后您需要在构造函数中进行自定义验证)。

【讨论】:

  • 在 Python 3.7 及更高版本中,数据类提供了一个很好的解决方案,解决了命名元组的一些丑陋问题,它们对于 DDD 值对象来说是完美的。
  • 我同意,从 2020 年开始,这是标准的方式。要拥有一个广泛的版本范围机制(2.7、3.3+,如果我记得的话),您可以使用 attrs 库,它实际上是创建 dataclasses 模块的蓝图。
  • namedtuples 有奇怪的继承语法......要求每个使用一个类的类都知道它是一个命名元组并使用__new__ 而不是__init__dataclasses没有这个限制
  • 我喜欢数据类。我唯一的问题是我无法将文档字符串附加到生成的属性。
【解决方案4】:

当您的老板期望 single responsibility principle 时,这很难,但 Pylint 拒绝了。所以向你的类添加第二个方法,这样你的类就违反了单一责任原则。在旁观者的眼中,您应该在多大程度上承担单一责任原则。

我的修复

我在我的类中添加了一个额外的方法,所以它现在做了两件事。

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

我只是想知道我现在是否需要将我的课程分成两个单独的文件,也许还有模块。

问题解决了,但不是我的同事整天都在争论规范,而不是继续下去,就像生死攸关一样。

【讨论】:

    猜你喜欢
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2019-10-23
    • 1970-01-01
    • 1970-01-01
    • 2012-10-01
    • 2010-10-01
    相关资源
    最近更新 更多