python类和对象
3.属性和方法
3.6初始化方法/构造方法
在Python中,构造方法(也称为初始化方法)是一个特殊的方法,用于在创建对象时初 始化对象的属性。这个方法在类定义中使用__init__
来表示。当你创建类的新实例时,__init__
方法会自动被调用,从而允许你为对象设置初始状态或执行其他必要的启动任务。
以下是一个简单的Python类示例,该类包含一个构造方法
class Person: def __init__(self, name, age): self.name = name # 设置对象的name属性 self.age = age # 设置对象的age属性 # 创建Person类的一个实例 person1 = Person("Alice", 30) # 访问对象的属性 print(person1.name) # 输出: Alice print(person1.age) # 输出: 30
3.7魔法方法
在Python中,魔术方法(也称为特殊方法或双下划线方法)是一类具有特殊意义的方法,它们的名称前后都包含两个下划线(例如,__init__
、__str__
、__add__
等)。这些方法为Python对象提供了丰富的功能,允许对象以特定的方式响应内置函数和操作符。
以下是一些常见的Python魔术方法及其用途:
- 初始化与表示:
__init__(self, ...)
:构造器,在对象创建时调用,用于初始化对象的属性。__new__(cls, ...)
:是一个静态方法,用于创建并返回类的实例。在__init__
之前被调用,通常用于继承不可变类型(如int、str、tuple)时自定义实例化过程。__del__(self)
:析构器,在对象被销毁时调用,用于执行清理操作。__str__(self)
:定义对象的“非正式”字符串表示,通常用于print()
函数。__repr__(self)
:定义对象的“正式”字符串表示,通常用于调试,repr()
函数会调用它。- 数值运算:
__add__(self, other)
:定义加法操作。__sub__(self, other)
:定义减法操作。__mul__(self, other)
:定义乘法操作。__truediv__(self, other)
:定义真除法操作。__floordiv__(self, other)
:定义整数除法操作。__mod__(self, other)
:定义取模操作。__pow__(self, other[, modulo])
:定义幂运算操作。__radd__(self, other)
:定义右侧加法操作(用于加法反射)。- 类似地,还有
__rsub__
、__rmul__
等,用于定义其他算术运算的反射操作。- 比较操作:
__lt__(self, other)
:定义小于操作。__le__(self, other)
:定义小于等于操作。__eq__(self, other)
:定义等于操作。__ne__(self, other)
:定义不等于操作。__gt__(self, other)
:定义大于操作。__ge__(self, other)
:定义大于等于操作。- 容器类型:
__len__(self)
:定义获取容器长度的操作(如len()
)。__getitem__(self, key)
:定义获取容器中元素的操作(如self[key]
)。__setitem__(self, key, value)
:定义设置容器中元素的操作。__delitem__(self, key)
:定义删除容器中元素的操作。__contains__(self, item)
:定义成员关系测试操作(如item in self
)。- 可迭代与迭代器:
__iter__(self)
:定义创建迭代器对象的操作。__next__(self)
:定义迭代器返回下一个项目的操作。
下面是一些例子帮助理解:
__str__
:返回对象的字符串表示,通常用于打印输出。class Point: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"({self.x}, {self.y})"
__len__
:返回对象的长度,通常与内置函数len()
一起使用。class MyList: def __init__(self, items): self.items = items def __len__(self): return len(self.items)
- 自定义迭代器类:通过定义
__iter__
和__next__
方法,赋予对象迭代能力class CustomIterator: def __init__(self, limit): self.current = 0 self.limit = limit def __iter__(self): return self def __next__(self): if self.current >= self.limit: raise StopIteration else: current_value = self.current self.current += 1 return current_value
4.继承/派生
在Python中,继承是一种面向对象编程(OOP)的特性,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。通过继承,子类可以重用父类的代码,同时还可以添加或覆盖父类的功能。
4.1基本用法
要创建一个子类,你需要使用class
关键字,并在类名后面的括号中指定父类。如果父类在当前模块或内置模块中,你可以直接写类名。如果父类在另一个模块中,你需要使用模块名加点号再加类名的方式。
# 定义父类 class Animal: def __init__(self, name): self.name = name def speak(self): raise NotImplementedError("Subclass must implement abstract method") # 定义子类,继承自Animal class Dog(Animal): def speak(self): return f"{self.name} says Woof!" # 创建Dog类的实例 d = Dog("Rex") print(d.speak()) # 输出: Rex says Woof!
在这个例子中,
Dog
类继承了Animal
类,并覆盖了speak
方法。当我们调用d.speak()
时,Python会查找Dog
类中定义的speak
方法,并调用它。
4.2多重继承
Python支持多重继承,即一个子类可以继承多个父类。多重继承可能会导致一些复杂的问题,比如方法名冲突(也称为菱形继承问题),但是Python的MRO算法通常能够很好地处理这些问题。
class A: def method(self): return "A's method" class B(A): pass class C(A): def method(self): return "C's method" class D(B, C): pass # 创建D类的实例 d = D() print(d.method()) # 输出: C's method,因为C在MRO中排在B之前
在这个例子中,
D
类继承了B
和C
,而B
和C
都继承了A
。当我们调用d.method()
时,由于C
的method
方法在MRO中排在B
的之前,所以Python会调用C
的method
方法。
- 继承可以提高代码的重用性和可维护性,但是过度使用继承可能会导致代码变得复杂和难以理解。
- 在设计类时,要考虑好继承关系,避免创建过于庞大的类层次结构。
- 如果子类需要修改父类的行为,可以考虑使用方法覆盖(重写父类的方法)或扩展(在子类中调用父类的方法,并添加额外的功能)。
4.3 覆盖 override
-
覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类的实例调用该方法时,实际调用的是子类中的覆盖版本,这种现象叫覆盖
-
作用:
-
实现和父类同名,但功能不同的方法
-
-
覆盖示例
class A: def work(self): print("A.work 被调用!") class B(A): '''B类继承自A类''' def work(self): print("B.work 被调用!!!") pass b = B() b.work() # 请问调用谁? B a = A() a.work() # 请问调用谁? A
5.封装
在Python中,封装(Encapsulation)是面向对象编程(OOP)的一个核心概念。它主要涉及到将对象的属性和方法结合在一起,并隐藏对象的内部实现细节,只对外暴露必要的接口。这样做的目的是增加代码的安全性和可维护性,同时减少不同部分之间的依赖。
-
隐藏内部实现:类的内部细节(如属性和方法)被隐藏起来,不对外部直接可见。这通常通过名称修饰(name mangling)或双下划线前缀(
__
)来实现,尽管Python并没有真正的私有变量或方法,但这是一种约定俗成的做法。 -
提供公共接口:类通过提供公共方法(也称为接口)来与外部世界交互。这些方法是类的外部用户唯一可以调用的。
-
保护对象状态:封装允许类控制对其属性的访问,从而保护对象的状态不被随意修改。这可以通过getter和setter方法来实现,它们分别用于获取和设置属性值。
在Python中,虽然没有严格的访问控制(如Java中的private
、protected
和public
关键字),但可以通过使用双下划线前缀:
class MyClass: def __init__(self, value): self.__secret_value = value # 修饰为“私有”属性 def get_secret_value(self): return self.__secret_value def set_secret_value(self, value): self.__secret_value = value
6.多态
在Python中,多态(Polymorphism)是面向对象编程(OOP)的一个核心概念,它允许不同的类对象通过相同的接口调用方法。多态性主要分为编译时多态(也称为静态多态或方法重载)和运行时多态(也称为动态多态或方法重写)。Python主要支持的是运行时多态,这是通过继承和方法重写来实现的。
6.1多的基本概念
多态意味着“多种形态”,在编程中,它允许一个接口或父类引用指向子类对象,并通过这个引用调用在子类中重写的方法。这样做的好处是,可以在不改变客户端代码的情况下,通过替换不同的子类来实现不同的行为。
6.2Python中的多态实现
在Python中,多态通常是通过继承和方法重写来实现的。以下是一个简单的例子:
class Animal: def speak(self): raise NotImplementedError("Subclass must implement abstract method") class Dog(Animal): def speak(self): return "Woof!" class Cat(Animal): def speak(self): return "Meow!" # 多态性示例 def animal_speak(animal): print(animal.speak()) # 创建Dog和Cat的实例 dog = Dog() cat = Cat() # 通过相同的接口调用方法 animal_speak(dog) # 输出: Woof! animal_speak(cat) # 输出: Meow!
面向对象编程语言的特征:
继承
封装
多态
7.方法重写
在Python中,方法重写(Method Overriding)是面向对象编程(OOP)中的一个重要概念。它允许子类提供一个特定的实现,该实现会覆盖(或重写)在父类中定义的同名方法。这样做的目的是允许子类根据自身的需要定制或扩展父类的行为。
7.1方法重写的基本步骤
-
定义一个父类,并在其中声明一个或多个方法。这些方法可以是具体的实现,也可以是抽象方法(在Python中,抽象方法通常通过在父类中引发
NotImplementedError
异常来模拟)。 -
创建一个子类,该子类继承自父类。
-
在子类中重写父类中定义的同名方法。这意味着子类提供了一个新的实现,该实现将替换父类中的实现。
以下是一个简单的Python示例,展示了方法重写的基本用法:
class Animal: def speak(self): return "说话" class Dog(Animal): def speak(self): return "Woof!" class Cat(Animal): def speak(self): return "Meow!" # 创建一个Dog实例并调用其speak方法 dog = Dog() print(dog.speak()) # 输出: Woof! # 创建一个Cat实例并调用其speak方法 cat = Cat() print(cat.speak()) # 输出: Meow!
7.2 内建函数重写
-
__abs__
abs(obj) 函数调用 -
__len__
len(obj) 函数调用 -
__reversed__
reversed(obj) 函数调用 -
__round__
round(obj) 函数调用 -
内建函数 重写示例
# file : len_overwrite.py class MyList: def __init__(self, iterable=()): self.data = [x for x in iterable] def __repr_(self): return "MyList(%s)" % self.data def __len__(self): print("__len__(self) 被调用!") return len(self.data) def __abs__(self): print("__len__(self) 被调用!") return MyList((abs(x) for x in self.data)) myl = MyList([1, -2, 3, -4]) print(len(myl)) print(abs(myl))
7.3运算符重载
-
运算符重载是指让自定义的类生成的对象(实例)能够使用运算符进行操作
-
运算符重载的作用
-
让自定义类的实例像内建对象一样进行运算符操作
-
让程序简洁易读
-
对自定义对象将运算符赋予新的运算规则
-
-
运算符重载说明:
-
运算符重载方法的参数已经有固定的含义,不建议改变原有的意义
-
- 算术运算符重载示例
class MyNumber: "此类用于定义一个自定义的类,用于演示运算符重载" def __init__(self, value): "构造函数,初始化MyNumber对象" self.data = value def __str__(self): "转换为表达式字符串" return "MyNumber(%d)" % self.data def __add__(self, rhs): "加号运算符重载" print("__add__ is called") return MyNumber(self.data + rhs.data) def __sub__(self, rhs): "减号运算符重载" print("__sub__ is called") return MyNumber(self.data - rhs.data) n1 = MyNumber(100) n2 = MyNumber(200) print(n1 + n2) print(n1 - n2)
8.super函数
在Python中,super()
函数是用于调用父类(超类)的一个方法。它常用于多继承的环境中,以确保每个父类都被正确地初始化,并且允许子类扩展或重写父类的方法。super()
解决了直接调用父类方法时可能遇到的名称解析问题,特别是在复杂的继承链中。
8.1基本用法
当在子类中调用super()
时,它会返回当前类的父类的一个临时对象,允许你调用该父类的方法。这通常用于__init__
方法来确保父类被正确初始化。
class Parent: def __init__(self): print("Parent __init__") class Child(Parent): def __init__(self): super().__init__() # 调用父类的__init__方法 print("Child __init__") # 创建Child类的实例 child = Child() # 输出: # Parent __init__ # Child __init__
在这个例子中,
Child
类的__init__
方法使用super().__init__()
来调用Parent
类的__init__
方法,确保父类被正确初始化。
8.2多继承中的用法
在多继承的情况下,super()
函数按照方法解析顺序(MRO,Method Resolution Order)来调用父类的方法。MRO是一个定义了在调用一个方法时应该搜索哪个类的顺序的算法。
class A: def __init__(self): print("A __init__") class B(A): def __init__(self): super().__init__() print("B __init__") class C(A): def __init__(self): super().__init__() print("C __init__") class D(B, C): def __init__(self): super().__init__() print("D __init__") # 创建D类的实例 d = D() # 输出可能(取决于Python版本和MRO的具体实现): # A __init__ # C __init__ # B __init__ # D __init__
在这个例子中,
D
类继承了B
和C
,而B
和C
都继承了A
。D
的__init__
方法使用super()
来调用它的一个父类的__init__
方法。由于MRO的存在,super()
会按照特定的顺序(在这个例子中可能是A -> C -> B -> D
)来调用方法。
8.3注意事项
super()
不是函数,而是一个类。当你调用super()
时,你实际上是在创建一个super
类的实例。super()
通常与__init__
方法一起使用,但也可以用于调用其他被重写的父类方法。- 在使用
super()
时,不需要传递任何参数;Python会自动处理这些。- 在单继承的情况下,
super()
的行为可能看起来与直接调用父类方法相同,但在多继承中,它提供了更强大和灵活的功能。
总之,super()
是Python中一个非常有用的特性,它简化了在继承环境中对父类方法的调用,并帮助避免了由于直接调用父类方法而可能导致的错误。