Oop 不是';t类成员变量和全局变量一样糟糕?

Oop 不是';t类成员变量和全局变量一样糟糕?,oop,global-variables,member,member-variables,Oop,Global Variables,Member,Member Variables,我们都知道,由于使用了全局变量,以下代码很糟糕: a = 3.14 def inc(): global a a += 1 inc() b = a 这太糟糕了,在python中,人们不得不故意声明global来“打破”禁令。但是,以面向对象的名义,下面的代码为什么是合理的: class A: def __init__(self): self.a = 3.14 def inc(self): self.a += 1 A_instance = A() A_ins

我们都知道,由于使用了全局变量,以下代码很糟糕:

a = 3.14

def inc():
  global a
  a += 1

inc()
b = a
这太糟糕了,在python中,人们不得不故意声明global来“打破”禁令。但是,以面向对象的名义,下面的代码为什么是合理的:

class A:
  def __init__(self):
    self.a = 3.14

  def inc(self):
    self.a += 1

A_instance = A()
A_instance.inc()
b = A_instance.a
在类实例的范围内,成员变量不是简单地与全局变量相同吗?“self”基本上允许任何成员函数不受限制地访问所有变量,无论其必要性如何,这使得它非常有状态,容易出错,并且难以读取(基本上是全局变量的所有不好的特性)

我们还知道,清洁和更好的方法是:

def inc(_a):
  return _a+1

a = 3.14
b = inc(a)

因此函数inc()至少是无状态的,并且具有可预测的行为。使用类并使其尽可能无状态的最佳实践是什么?或者根据定义,OO是否总是有状态的,并且必须在没有类的情况下进行函数式编程?我还听人说@static方法很愚蠢,破坏封装,应该避免…

现在之所以认为
self
的概念不算坏的做法,是因为如果要创建一个类,该类中的所有内容都应该以某种方式关联,通常是通过
\uuuu init\uuuu
dunder方法初始化的类属性。基本上,如果您要创建一个类,那么应该有某种链接,一个类全局,如果您愿意的话,它是类属性

通常的想法是,像这样用self创建类

class A:
  def __init__(self):
    self.a = 3.14

  def inc(self):
    self.a += 1
这并不是人们经常看到的事情,而是人们选择了在许多人看来更干净、更可预测的方式

class A:
  def __init__(self, a):
    self.a = a

  def inc(self):
    self.a += 1
通过这种方式,当您声明类实例时,您可以更好地控制实际存在的内容,因此类实例是用变量声明的

A_instance = A(3.14)   #a will be 3.14 in this instance
B_instance = A(3.142)  #a will be 3.142 in this instance
等等。 在
\uuuu init\uuuu
函数外部声明但在类内部声明的类变量,其本身有很大的不同,如下所示

class A:
  a = 22/7

  def __init__(self):
      ....
class A:
    a = 22/7

    def __init__(self, item):
        self.item = item

A_instance = A(3.14)
B_instance = A(5)
有了它,a将成为整个类(不是实例而是类本身)的全局变量。那看起来像什么

class A:
  a = 22/7

  def __init__(self):
      ....
class A:
    a = 22/7

    def __init__(self, item):
        self.item = item

A_instance = A(3.14)
B_instance = A(5)
虽然
A_实例
B_实例
完全不同,
A_实例。项
将等于3.14,而
B_实例。项
将等于5,这两个项都将类变量
A
作为22/7

因此,类变量在该类的所有实例中保持一致/相同,除非它被主动更改


关键是,如果你要创建一个类,它应该有特定的变量,这些变量相互交织,影响所有特定的函数组,这些是你在类方法中使用的函数。如果使用得当,
self
在类中的使用是正确的。现在,
self
的概念不被认为是不好的做法的原因是,如果你要创建一个类,那么该类中的所有内容都应该以某种方式相关,而这种方式通常是错误的类实例通过
\uuuu init\uuuu
dunder方法初始化的类属性。基本上,如果您要创建一个类,那么应该有某种链接,一个类全局,如果您愿意的话,它是类属性

通常的想法是,像这样用self创建类

class A:
  def __init__(self):
    self.a = 3.14

  def inc(self):
    self.a += 1
这并不是人们经常看到的事情,而是人们选择了在许多人看来更干净、更可预测的方式

class A:
  def __init__(self, a):
    self.a = a

  def inc(self):
    self.a += 1
通过这种方式,当您声明类实例时,您可以更好地控制实际存在的内容,因此类实例是用变量声明的

A_instance = A(3.14)   #a will be 3.14 in this instance
B_instance = A(3.142)  #a will be 3.142 in this instance
等等。 在
\uuuu init\uuuu
函数外部声明但在类内部声明的类变量,其本身有很大的不同,如下所示

class A:
  a = 22/7

  def __init__(self):
      ....
class A:
    a = 22/7

    def __init__(self, item):
        self.item = item

A_instance = A(3.14)
B_instance = A(5)
有了它,a将成为整个类(不是实例而是类本身)的全局变量。那看起来像什么

class A:
  a = 22/7

  def __init__(self):
      ....
class A:
    a = 22/7

    def __init__(self, item):
        self.item = item

A_instance = A(3.14)
B_instance = A(5)
虽然
A_实例
B_实例
完全不同,
A_实例。项
将等于3.14,而
B_实例。项
将等于5,这两个项都将类变量
A
作为22/7

因此,类变量在该类的所有实例中保持一致/相同,除非它被主动更改


关键是,如果你要创建一个类,它应该有特定的变量,这些变量相互交织,影响所有特定的函数组,这些是您在类方法中使用的函数,因此在类中使用
self
是正确的,如果使用得当

这取决于您是否需要保存状态。如果您确实需要保存状态,那么它需要保存在某个地方。类允许您将状态与操作该状态的方法一起保存,并强制执行一个类本地作用域,该类本地作用域比在全局作用域中存储状态更不容易出错

作为一个具体的例子,我最近为一个森林模拟实现了一个树类。与树关联的一个重要变量是它的年龄。树的树龄应该存放在哪里?将其存储在树类内比存储在树类外更有意义

如果保护树的年龄不被类外的代码操纵是重要的,那么我可以使用Python优秀的
@property
语法来实现这一目的,或者更直接地通过Python
\u varname
变量命名约定来实现


在类中,是的,树的年龄对其他方法是可见的,但这是一个相对狭窄的范围,如果您的类是按照面向对象的设计原则编写的,则肯定与全局范围不同。

这取决于您是否需要保存状态。如果您确实需要保存状态,那么它需要