类与对象
到目前为止你使用的每种类型(字符串、列表、字典)实际上都是一个类。当你调用 "hello".upper() 时,就是在调用字符串对象上的一个方法。类让你能够定义自己的类型,拥有自己的数据和行为。一个 Player 类可以保存名字、分数和等级,并知道如何显示自己。
蓝图和实例
类是一个蓝图。实例是根据该蓝图创建的具体事物。你可以根据需要创建任意多个实例,每个实例都有自己的数据,但共享类中定义的相同方法。
class Dog:
def bark(self):
print("Woof!")
rex = Dog()
luna = Dog()
rex.bark() # "Woof!"
luna.bark() # "Woof!"Dog 是类。rex 和 luna 是实例:两只不同的狗,每只都共享类中定义的相同行为。
__init__ 和 self
__init__ 是创建新实例时 Python 自动调用的方法。它是你为对象设置初始数据的地方。self 是方法引用其正在操作的具体实例的方式,它始终是第一个参数。
class Player:
def __init__(self, name, score=0):
self.name = name
self.score = score
def add_points(self, points):
self.score += points
def display(self):
print(f"{self.name}: {self.score} points")
alice = Player("小明")
bob = Player("小红", score=50)
alice.add_points(30)
alice.display() # "小明: 30 points"
bob.display() # "小红: 50 points"self.name 和 self.score 是实例属性:它们属于具体的对象,而不是类本身。每个 Player 实例都有自己的 name 和 score。
方法
类内部定义的任何函数都是方法。实例方法的第一个参数始终是 self;Python 会自动传入它。方法可以通过 self 读取和修改实例的数据。
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def scale(self, factor):
self.radius *= factor
return self # 返回 self 允许链式调用:c.scale(2).scale(0.5)
c = Circle(5)
print(c.area()) # 78.53975
c.scale(2)
print(c.area()) # 314.159类变量与实例变量
直接定义在类上(不在 __init__ 内部)的变量是类变量。所有实例共享同一个类变量。在 __init__ 中设置在 self 上的变量是实例变量,对每个对象都是唯一的。
class Player:
max_lives = 3 # 类变量,对每个 Player 都相同
def __init__(self, name):
self.name = name # 实例变量,每个 Player 各不相同
self.lives = Player.max_lives
def die(self):
self.lives -= 1
alice = Player("小明")
bob = Player("小红")
Player.max_lives = 5 # 改变所有当前和未来的实例类变量用于在所有实例之间共享的值:常量、计数器、默认值。实例变量用于每个对象不同的数据。
__str__ 和 __repr__
__str__ 控制 print() 和 f-字符串显示对象的内容。__repr__ 控制控制台和调试时显示的开发者视图。始终定义 __repr__。当你希望有一个独立于调试视图的、面向用户的清晰显示时,再定义 __str__。
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return f"{self.name} ({self.score} pts)"
def __repr__(self):
return f"Player(name={self.name!r}, score={self.score})"
alice = Player("小明", 87)
print(alice) # "小明 (87 pts)" (uses __str__)
repr(alice) # "Player(name='小明', score=87)" (uses __repr__)始终定义 __repr__。当你希望有一个独立于调试视图的、面向用户的清晰表示时,再定义 __str__。如果只定义了 __repr__,Python 会将其同时用于两者。
私有约定
Python 没有真正的私有变量,但名称开头的单个下划线(_balance)是一种约定,表示"这是内部使用的,不要从类外部直接使用"。语言本身不强制执行;它只是与其他开发者的沟通方式。
class BankAccount:
def __init__(self, balance):
self._balance = balance # _ 表示"勿动"
def deposit(self, amount):
if amount > 0:
self._balance += amount
def balance(self):
return self._balance双下划线(__name)触发名称改写;Python 将属性重命名为 _ClassName__name 以避免子类中的冲突。这种用法很少需要。单下划线在大多数代码中都是约定。
继承
一个类可以从另一个类继承,自动获得其所有属性和方法。然后你可以在子类中重写特定方法以改变其行为。这让你能够重用一个公共基类,并在需要的地方进行特化。
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
pets = [Dog("旺财"), Cat("小花"), Dog("大黄")]
for pet in pets:
print(pet.speak())Dog 和 Cat 继承了 Animal 的 __init__,因此它们不需要自己的 __init__。它们用特定的行为重写了 speak()。
super()
super() 调用父类的方法。当你想要扩展父类的行为而非完全替换它时使用它:调用父类的 __init__ 来运行其设置代码,然后再添加子类需要的内容。
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
class Dog(Animal):
def __init__(self, name):
super().__init__(name, "Woof") # 调用 Animal.__init__
self.tricks = [] # 添加一些额外的东西
def learn(self, trick):
self.tricks.append(trick)
rex = Dog("旺财")
rex.learn("sit")
print(rex.tricks) # ["sit"]当你的子类有自己的 __init__ 且父类也有时,始终调用 super().__init__()。
类方法和静态方法
@classmethod 创建一个接收类本身而非实例的方法。它适用于备用构造器:从字符串、文件或其他格式创建实例。@staticmethod 是出于组织原因放在类内部的普通函数;它既不接收实例也不接收类。
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
@classmethod
def from_string(cls, data):
name, score = data.split(",")
return cls(name, int(score))
alice = Player.from_string("小明,87")class Player:
@staticmethod
def is_valid_name(name):
return name.isalpha() and len(name) >= 2
Player.is_valid_name("小明") # True
Player.is_valid_name("A1") # False使用 @classmethod 实现备用构造器。使用 @staticmethod 实现逻辑上属于类但不需要实例或类数据的工具函数。
@property
@property 让你能够像访问属性一样访问方法,无需括号。用于从其他属性计算得出、并且作为简单属性访问感觉自然的值。
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius ** 2
@property
def diameter(self):
return self.radius * 2
c = Circle(5)
print(c.area) # 78.53975 (看起来像属性,运行起来像方法)
print(c.diameter) # 10属性对于计算值很有用:从其他属性派生的、感觉无需 () 就能自然访问的东西。
实战
一个具有实例属性、方法、@property 和 __str__ 的 Player 类:
class Player:
max_lives = 3
def __init__(self, name: str):
self.name = name
self.score = 0
self.lives = Player.max_lives
def earn_points(self, amount: int) -> None:
self.score += amount
def take_hit(self) -> bool:
self.lives -= 1
return self.lives > 0
@property
def is_alive(self) -> bool:
return self.lives > 0
def __str__(self) -> str:
return f"{self.name} | Score: {self.score} | Lives: {self.lives}"
alice = Player("小明")
alice.earn_points(50)
alice.take_hit()
print(alice) # "小明 | Score: 50 | Lives: 2"
print(alice.is_alive) # True
