在类中使用getProperty()而不是self.property?

时间:2018-06-27 05:05:06

标签: python

我不知道正确的用语,所以在网上找不到关于此的任何信息。

使用以下示例代码:

  public void convert2JSON(URL inputJson, File outputPojoDirectory, String packageName, String className) throws IOException
   {  
          JCodeModel codeModel = new JCodeModel();  
          URL source = inputJson;  
          GenerationConfig config = new DefaultGenerationConfig()
     {  
          @Override  
          public boolean isGenerateBuilders() 
          { // set config option by overriding method  
            return true;  
          }  
          public SourceType getSourceType()
          {  
            return SourceType.JSON;  
          }  

     };  
          SchemaMapper mapper = new SchemaMapper(new RuleFactory(config, new Jackson2Annotator(config), new SchemaStore()), new SchemaGenerator());  
          mapper.generate(codeModel, className, packageName, source);  
          codeModel.build(outputPojoDirectory);  
   }

现在,说我想检查一下水果是否为红色:

def Fruit(object):
    def __init__(self, color):
        self._color = color
    def color(self):
        return self._color

可以很好地工作。但是

    def isRed(self):
        if self._color == "red":
            return True
        return False

具有 def isRed(self): if self.color() == "red": return True return False 函数是一种好习惯吗? (我假设这是因为我正在攻读的是MIT教授的课程,他在课堂上这样做,并希望学生在功课上也做同样的事情。)

这两个示例中的任何一个都不同吗?为什么习惯用getProperty来简单地引用该属性呢?

编辑:添加了下划线以使self.property成为惯例。

2 个答案:

答案 0 :(得分:1)

TL; DR:并非所有常规编程最佳实践都不是Python最佳实践。 Getter和setter方法是常规(OOP)最佳实践,但不是Python最佳实践。相反,请尽可能使用普通的Python属性,并根据需要切换到Python @property

它有许多面向对象的编程语言(例如Java和C ++),它被认为是一种很好的做法:

  • 将数据成员(也称为“属性”)设为私有
  • 提供getter和/或setter方法以访问它们

为什么?

  • 通过“封装” 启用更改,方法是保持接口稳定,同时保持实现的灵活性(解耦)
  • 允许更细致的访问级别

让我们详细了解这些:

面向对象的“封装”

面向对象的核心思想之一是,将小数据块的定义与与该数据相关的功能捆绑在一起,使命令式/“结构化” /程序化程序更易于管理和发展。

这些捆绑包称为“对象”。每个“类”都是具有相同数据结构(虽然可能具有不同的数据)和相同相关功能的组对象的模板。

数据定义是类(对象的“属性”)的(非静态)数据成员。相关功能被编码为功能成员(“方法”)。

这也可以被视为构建新的用户定义类型的方法。 (每个类都是一个类型,每个对象有点像一个值。)

通常,与已经提供的数据成员的类型相比,方法需要更多有关属性值的保证才能正常工作。假设您有

class Color() {
    float red;
    float green;
    float blue;

    float hue() {
        return // ... some formula
    }

    float brightness {
        return // ... some formula
    }
}

如果redgreenblue的范围是[0,1],则这些方法的实现可能取决于该事实。同样,如果它们在[0,256)范围内。不管类内部的约定如何,维护该类的方法都是它的任务,并且仅将值分配给可接受的数据成员。

尽管通常,对于有意义的面向对象程序,不同类的对象必须进行交互。但是您不希望仅仅因为您正在访问其他类而考虑其他类的内部约定,因为这将需要大量查找才能找出那些约定。因此,您不应从该类之外的代码中为该类的对象分配数据成员。

为避免这种错误或过失,在这些语言中广为接受的最佳实践是将所有数据成员声明为私有。但这意味着它们也不能从外面读取!如果外部不关心该值,则可以通过提供一种非私有的getter方法来解决此问题,该方法除了提供属性值外什么也不做。

在限制涟漪效应的同时实现变化

说外面的东西(例如另一个类)必须能够设置类的某些属性的值。并且说,除了该属性的类型已经施加的限制之外,没有任何其他限制。 您应该将该属性设为公开吗?(仍然假设在Python中不是 !)否!,相反,提供了一个setter方法除了将值作为参数并将其分配给属性外什么也没做!

似乎有点沉闷,那为什么呢?这样我们以后可以改变主意!

新的副作用

假设您每次颜色对象的红色分量发生更改时都要登录到控制台/终端(标准输出)。 (出于任何原因。)

在(setter)方法中,添加一行代码,然后执行此操作,而无需调用方进行任何更改。

但是,如果您需要首先从分配给公共属性切换到调用setter方法,那么所有为这些属性分配的代码段(到那时可能很多)也必须更改! (不要忘记将属性设为私有,这样就不会遗忘任何属性。)

因此,最好从一开始就只具有私有属性,并在类外的代码必须能够设置值时添加setter方法。

内部代表的变更

假设您刚刚注意到,对于您的应用程序,颜色实际上应该在内部以色相,值和饱和度表示,而不是红色,绿色和蓝色分量。

如果您有setter和getter方法,则由于必要的转换计算,红色,绿色和蓝色的方法将变得更加复杂。 (但是,亮度和色调方法将变得更加简单。)不过,与必须更改使用该类的类之外的所有代码相比,更改它们的工作量要少得多。由于界面保持不变,呼叫者根本不需要更改,也不会发现任何差异。

但是,如果您需要首先从分配给公共属性转换为调用setter方法,那么……我们去过那里,不是吗?

解耦

因此,访问器方法方法(我们称为getter和setters)可帮助您将类的公共接口与其内部实现分离,从而将其对象与用户分离。这样,您就可以在不破坏公共接口的情况下更改内部实现,从而不必在使用类时更改使用类的代码。

粒度访问级别

需要一个只能从外部读取而不能从外部写入的属性吗?简便:仅提供 getter 方法,但不提供setter方法(并且属性本身是私有的)。

不常见,但比您想像的更常见:

需要一个只能从外部只能写但不能从外部读取的属性吗?简便:仅提供 setter方法,但不提供getter方法(并且属性本身是私有的)。

不确定是否应该从班级外部访问(和访问)您的属性? 将其设为私有,并且不要暂时提供任何获取和设置方法。您以后可以随时添加它们。 (然后然后考虑他们应该具有的可见度级别。)

如您所见,没有理由在可变对象中拥有非私有属性。 (假设运行时开销对您的应用程序不重要(实际上可能不重要),或者由编译器进行了优化(可能至少部分地)。)

不是安全功能!

请注意,属性和方法的“可见性”级别并不意味着提供应用程序安全性或隐私性(不是)。它们是帮助程序员犯错误的工具(通过避免犯错误,避免他们偶然访问不应被他们访问的东西),但它们不会阻止对抗性程序员访问这些东西。或者,就此而言,诚实的程序员认为自己知道自己在做什么(无论他们是否知道),并且愿意冒险。

Python与众不同

在Python中,一切都是公开的

尽管Python也是强制性的,“结构化的”,过程性的且非常面向对象的,但它采用了更为宽松的可见性方法。 Python中没有真正的“私有”可见性级别,也没有“受保护”或“包”(Java中的默认)级别。

本质上, Python类中的所有内容都是公开的

当将Python用作快速,肮脏的即席解决方案的脚本语言时,这很有意义,您可能会编写一次代码,然后扔掉(或者不做进一步开发就保持这种状态)。

如果您使用Python开发了更多涉及的应用程序(并且使用Python肯定是可行的,并且也做了很多事情),则可能需要区分类的公共接口及其内部实现细节。 Python提供了两个级别的“隐藏”内部成员(函数,数据属性):

  • 按照惯例:_前缀
  • 按名称修饰:__前缀

按照惯例“隐藏”

_开头的名称向命名空间之外的所有人(无论是类,模块还是程序包)发出信号:

  

除非您知道自己在做什么,否则不应该访问它。我(该名称空间中东西的实现者)可能会随意更改它,因此您可能不知道通过访问它将要做什么。 东西可能会破裂。如果是的话,那将是您的(访问它的那个)错误,而不是我的(实现它的那个)。该成员不属于此命名空间的公共接口

     

是的,您可以访问它。 这并不意味着您应该。我们都是成年人。负责。

即使您还不是成年人,也应该坚持这一点。

隐藏名称修改

__开头的名称向命名空间之外的所有人(无论是类,模块还是程序包)发出信号:

  

仅与_相同,甚至更强

此外,并且仅当名称空间 是一个类(且属性名称以不超过一个下划线结尾)时:

为确保您不会意外地从外部访问这些内容,请使用Python "mangles" the names of these attributes从类外部进行访问。结果名称是完全可预测的(_ +(简单的类名称+原始属性名称),因此您仍然仍然可以访问这些内容,但是您肯定不会简单地误认为

此外,此可以有助于避免基类成员及其子类成员之间的名称冲突。 (但是,如果类共享相同的类名(如使用“简单类名”,不包括模块和包),则将无法正常工作。)

在任何一种情况下,您可能都有充分的理由无论如何都要访问这些值(例如,用于调试),并且Python不想在这样做时妨碍您的工作(或使用名称修改功能,最多只能稍微改变一下)。< / p>

Python具有基于方法的“属性”,可以像访问数据属性一样对其进行访问

因此,由于Python中没有真正的私有工具,因此我们无法应用Java和C ++中的模式/样式。但是我们可能仍然需要稳定的接口来进行认真的编程。

在Python中,您可以 用方法替换data属性,而不必更改其用户。 Pils19's answer提供了一个示例:

class Fruit(object):
    def __init__(self, color):
        self._color = color

    @property
    def color(self):
        return self._color

(此装饰器here的文档。)

如果我们还提供了一个属性设定器方法和一个属性设定器方法...

class Fruit(object):
    def __init__(self, color):
        self._color = color

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, c):
        self._color = c

    @color.deleter
    def color(self):
        del self._color

这将相当于一个简单的data属性:

class Fruit(object):
    def __init__(self, c):
        self.color = c

但是现在我们拥有方法的所有自由。我们可以忽略其中的任何一个(最常见的是只具有getter,因此您具有只读属性),我们可以赋予它们其他或不同的行为,等等。

这是Python中推荐的方法:

  • 如有疑问,请使用(公共)数据成员
  • 带有_的前缀以获取实施细节
  • 如果/当您需要其他/不同的行为或禁用读取,写入或删除,使用属性或将公共数据成员替换为属性时

您的教授

  

我假设[有一种在Python中定义非属性获取器和设置器的良好做法],因为我正在学习的MIT教授的课程与他的课程相同,并希望学生也能做到这一点在他们的作业上。

您确定这是您的教授所做的,还是他使用了Python的属性机制?

如果他这样做了,这是关于Python的一类,还是恰好发生在示例中使用Python(而您的教授也使用它来演示实际上仅适用于其他语言的东西)?

我们不要忘记:即使是麻省理工学院的教授,也可能被迫教授并非该学科各个方面的专家的课程。

答案 1 :(得分:0)

通常,@Property装饰器对您来说是一个好习惯。并具有带有单个下划线的内部属性。对于您来说,它看起来像:

class Fruit(object):
    def __init__(self, color):
        self._color = color

    @property
    def color(self):
        return self._color