【Python】哪些类型的变量应该放在__init__方法(构造函数)中

0起因

最近,在刷题的过程中思考如何把代码的向量命名规范化,进一步的希望规范化完整的代码功能和设计模式。

以 leetcode第3题:https://leetcode.cn/problems/longest-substring-without-repeating-characters/为例。

正常代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
n = len(s)
char_index = {} # Maps character to its last position
max_length = 0
start = 0 # Start of current substring

for i in range(n):
# If character is already in the current substring, update start position
if s[i] in char_index and char_index[s[i]] >= start:
start = char_index[s[i]] + 1
else:
# Update max_length if current substring is longer
max_length = max(max_length, i - start + 1)

# Update last position of character
char_index[s[i]] = i

return max_length

这是通常的LeetCode格式,没有问题。

但是,如果想定义一个构造函数__init__初始化向量那应该怎么做呢?

于是,按照逻辑和想法写了一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
def __init__(self):
self.lst = {}
self.longest_substr = 0
self.start = 0

def lengthOfLongestSubstring(self, s: str) -> int:
n = len(s)
for i in range(n):
if s[i] in self.lst and self.lst[s[i]] >= self.start:
self.start = self.lst[s[i]] + 1
else:
self.longest_substr = max(self.longest_substr, i - self.start + 1)
self.lst[s[i]] = i
return self.longest_substr

写完之后,诞生一个新的问题:能不能把 n 这个长度向量也放在初始化中呢?

答案是不行,更应该说是“不规范”。

1以下类型的变量放在__init__

__init__ 方法(构造函数)用于初始化一个类的实例的状态。

因此,你应该把以下类型的变量放在 __init__ 中:

  1. 属于对象本身的属性 (Attributes that belong to the object itself):

    • 这些变量描述了对象的特征或状态,并且在对象的整个生命周期内都可能被使用。

    • 它们对于每个对象实例来说可能是不同的。

    • 例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      class Dog:
      def __init__(self, name, breed, age):
      self.name = name # 属于狗的名字
      self.breed = breed # 属于狗的品种
      self.age = age # 属于狗的年龄

      my_dog = Dog("Buddy", "Golden Retriever", 3)
      your_dog = Dog("Lucy", "Labrador", 5)
      # my_dog 和 your_dog 有不同的 name, breed, age
  2. 对象创建时必须提供的参数 (Parameters required when creating the object):

    • 这些参数是创建对象时不可或缺的,用于设置对象的初始状态。
    • 它们通常被用来初始化对象的属性。
    • 例子(和上面一样):name, breed, age 是创建 Dog 对象时必须提供的。
  3. 对象内部使用的,但不需要外部直接访问的变量 (Variables used internally by the object, but not directly accessed externally):

    • 这些变量通常用于存储对象的内部状态,或者作为对象方法的辅助变量。

    • 它们通常以下划线 _ 开头(例如 self._internal_counter),这是一种约定,表示它们是“私有的”或“受保护的”,不建议从对象外部直接访问。

    • 例子:

      1
      2
      3
      4
      5
      6
      7
      class Circle:
      def __init__(self, radius):
      self.radius = radius
      self._area = 3.14159 * radius * radius # 内部计算的面积,不直接暴露

      def get_area(self): # 提供一个方法来获取面积
      return self._area
  4. 与对象关联的其他对象 (Other objects associated with the object):

    • 如果一个对象需要与其他对象协作,那么这些其他对象的引用可以作为属性存储在 __init__ 中。 这体现了对象之间的“has-a”关系(组合或聚合)。

    • 例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      class Engine:
      def __init__(self, horsepower):
      self.horsepower = horsepower

      class Car:
      def __init__(self, make, model, engine):
      self.make = make
      self.model = model
      self.engine = engine # Car "has-a" Engine

      my_engine = Engine(200)
      my_car = Car("Toyota", "Camry", my_engine)
  5. 默认值属性 (Attributes with default values):

    • 如果某些属性在对象创建时可以不提供,你可以给它们设置默认值。

    • 例子:

      1
      2
      3
      4
      5
      6
      7
      class Rectangle:
      def __init__(self, width, height=1): # height 有默认值 1
      self.width = width
      self.height = height

      rect1 = Rectangle(5) # height 使用默认值 1
      rect2 = Rectangle(5, 10) # height 被指定为 10

不应该放在 __init__ 中的变量:

  • 局部变量 (Local variables): 只在某个方法内部使用的临时变量,不属于对象的状态,应该在方法内部定义。

  • 与特定方法调用相关的变量 (Variables related to a specific method call): 例如,lengthOfLongestSubstring 方法中的 n,它是输入字符串 s 的长度,与对象本身的状态无关,应该在方法内部计算。

  • 类级别的变量 (Class-level variables): 这些变量属于类本身,而不是类的实例。它们应该在类定义中直接定义,而不是在 __init__ 中。 类变量在所有实例之间共享。

    1
    2
    3
    4
    5
    class MyClass:
    class_variable = 0 # 类变量

    def __init__(self, instance_variable):
    self.instance_variable = instance_variable # 实例变量

2总结

__init__ 的目的是初始化对象的状态。

变量是否应该放在 __init__ 中,取决于它是否是对象持久状态的一部分,或者是否在对象创建时必须提供。

清晰地区分对象的属性(放在 __init__ 中)和方法的局部变量(放在方法内部)对于编写清晰、可维护的代码至关重要。

在LeetCode第三题中,n与特定的调用方法相关而且与对象本身状态无关,应该在方法内部计算。