面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
面向对象的设计思想是抽象出Class,根据Class创建Instance。
类和实例
类的定义
类(Class)是面向对象编程(OOP)中的一个核心概念。它是一种数据结构,用于定义对象的蓝图或模板。通过类,可以创建具有特定属性和方法的对象,并对对象进行操作。类不仅定义了对象的属性(通常通过类的变量表示),还定义了对象能执行的行为(通常通过类的方法表示)。
class Person:
# 类的属性和方法
pass
class后面紧接着是类名,即Person,类名通常是大写开头的单词
类的属性
类的属性是对象的特征,通常是类中的变量。例如,可以为一个 Student 类添加 name 和 age 属性:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
初始化方法 init:这是一个特殊的方法,每次创建类的实例时,都会自动调用它。它用于初始化对象的属性。
__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
⚠️特殊方法__init__前后分别有两个下划线!!!
类的方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"Hello, my name is {self.name} and I am {self.age} years old.")
在Student类的内部定义访问数据的函数greet,这样,就把“数据”给封装起来了。
创建实例
定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的:
person1 = Person("Alice", 30) # 创建一个 Person 类的实例
person1 .greet() # 调用实例方法,调用时self参数不用传递
# 输出结果:
Hello, my name is Alice and I am 30 years old.
类的封装
封装是将数据和操作数据的代码组合在一起,限制对对象内部状态的访问。通过方法来控制外部对对象属性的访问,而不是直接访问属性。
从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、age属性:
person1 = Person("Alice", 30)
print(person1 .age)
person1.age = 20
print(person1.age)
30
20
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
def greet(self):
print(f"Hello, my name is {self.__name} and I am {self.__age} years old.")
person1 = Student("Alice", 20)
print(person1.__age)
person1.greet() # 调用实例方法
# 外部访问student1.__age会报错:
Traceback (most recent call last):
File "D:\Code\work\pythonProject\test.py", line 12, in <module>
print(person1.__age)
^^^^^^^^^^^^^^
AttributeError: 'Person' object has no attribute '__age'
# 外部调用Student的方法:
Hello, my name is Alice and I am 20 years old.
这样封装之后外部就不可以修改内部数据。
如果希望外部访问Student类的内部数据或者外部进行更改数据,可以在类里定义相关方法,外部调用方法实现。
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
def greet(self):
print(f"Hello, my name is {self.__name} and I am {self.__age} years old.")
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
student1 = Person("Alice", 20) # 创建一个 Person 类的实例
student1.greet() # 调用实例方法
print(student1.get_name())
print(student1.get_age())
student1.set_age(25)
print(student1.get_age())
Hello, my name is Alice and I am 20 years old.
Alice
20
25
将修改数据的操作定义在类的方法里可以在方法中对参数进行检查,避免无效数据:
# 将上面的set_age函数添加判断条件:
def set_age(self, age):
if age < 0:
raise ValueError("Age cannot be negative.")
self.__age = age
调用set_age,传入参数-3:
student1.set_age(-3)
print(student1.get_age())
Traceback (most recent call last):
File "D:\Code\work\pythonProject\test.py", line 25, in <module>
student1.set_age(-3)
File "D:\Code\work\pythonProject\test.py", line 17, in set_age
raise ValueError("Age cannot be negative.")
ValueError: Age cannot be negative.
类的继承
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。子类继承父类的属性和方法,子类可以扩展或重写父类的方法。
class Student(Person): # 继承自 Person 类
def __init__(self, name, age, grade):
super().__init__(name, age) # 调用父类的 __init__ 方法
self.grade = grade # 新增属性
def greet(self):
print(f"Hello, I am {self.name}, {self.age} years old, and I am in grade {self.grade}.")
类的多态
当子类和父类都存在相同的方法时,我们说,子类的方法覆盖了父类的方法,在代码运行的时候,总是会调用子类的方法。这样,我们就获得了继承的另一个好处:多态。
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
def make_animal_speak(animal):
animal.speak()
dog = Dog()
cat = Cat()
make_animal_speak(dog) # 输出 "Woof!"
make_animal_speak(cat) # 输出 "Meow!"
实例属性和类属性
给实例绑定属性的方法是通过实例变量,或者通过self变量:
class Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
直接在class中定义属性,这种属性是类属性,归Student类所有:
class Student(object):
name = 'Student'
class Student(object):
count = 0
def __init__(self, name):
self.name = name
Student.count += 1
# 测试:
if Student.count != 0:
print('测试失败!')
else:
bart = Student('Bart')
print('Students:', Student.count)
lisa = Student('lisa')
print('Students:', Student.count)
Students: 1
Students: 2
使用__slots__
slots__只能定义在类定义中,用于限定该类能添加的属性,如果定义了__slots,那么实例化对象只能添加__slots__中定义的属性,否则会抛出AttributeError异常。
class Student(object):
count = 0
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
class Foo(Student):
pass
s = Student()
s.name = 'Michael'
print(s.name)
print(Student.__slots__)
print(s.__slots__)
stu = Foo()
stu.name = 'test'
print(stu.name)
stu.age = 12
print(stu.age)
stu.score = 99
print(stu.score)
print(stu.__slots__)
print(Student.__slots__)
Michael
('name', 'age')
('name', 'age')
test
12
99
('name', 'age')
('name', 'age')
使用@property
在设置属性值时,既想检查数据正确性又想简便实现,可以使用@property装饰器
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('成绩必须是整数!')
if value < 0 or value > 100:
raise ValueError('成绩必须在0 ~ 100之间!')
self.__score = value
s = Student('Bob', 59)
print(s.score) # 输出:59
s.score = 60
print(s.score) # 输出:60
s.score = 'p' # ValueError: 成绩必须是整数!
s.score = 120 # ValueError: 成绩必须在0 ~ 100之间!
多重继承
一个子类可以继承多个父类,从而获得多个父类的属性方法。
例如:
class Animal(object):
pass
# 大类:
class Mammal(Animal): # 哺乳动物
pass
class Bird(Animal): # 鸟
pass
# 其他功能类:
class Runnable(object): # 能跑
def run(self):
print('Running...')
class Flyable(object): # 能飞
def fly(self):
print('Flying...')
# 各种动物:
class Dog(Mammal, Runnable): # 狗狗,哺乳动物且能跑
pass
class Bat(Mammal, Flyable): # 蝙蝠,哺乳动物且能飞
pass
class Parrot(Bird, Flyable): # 鹦鹉,鸟类且能飞
pass
class Ostrich(Bird, Runnable): # 鸵鸟,鸟类且能跑
pass
枚举类
枚举(Enum)类用于定义一组命名的常量,这些常量在逻辑上是相互关联的。
定义一组月份常量:
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 枚举所有成员:
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
# 输出:
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
value属性则是自动赋给成员的int常量,默认从1开始计数。
从Enum派生出自定义类:
from enum import Enum
class Status(Enum):
ACTIVE = 1
INACTIVE = 0
PENDING = 3
SUSPENDED = 4
# 通过名称访问
print(Status.ACTIVE)
# 输出: Status.ACTIVE
# 通过值访问
print(Status(1))
# 输出: Status.ACTIVE
print(Status.ACTIVE.name) # 输出: ACTIVE
print(Status.ACTIVE.value) # 输出: 1
for status in Status:
print(status)
# 输出:
Status.ACTIVE
Status.INACTIVE
Status.PENDING
Status.SUSPENDED
简单应用:创建学生实例,不用输入字符串添加学生性别
Gender = Enum('Gender', ('Male', 'Female'))
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = Gender(gender)
# 测试:
bart = Student('Bart', 1)
print(bart.gender)
#输出:
Gender.Male
元类
元类(Metaclass)是用于创建类的 “类”。简单来说,元类控制类的创建和行为,就像类控制实例的创建和行为一样。通过元类,你可以定制类的创建过程、添加或修改类的属性和方法,以及实现自定义的类行为。
元类的作用
元类的主要作用是在类创建时,自动地对类进行修改或增强。它的常见用途包括:
1.自动注册类:在创建类时自动把类加入某个注册表中。
2.验证类的属性:检查类是否包含某些特定的属性和方法。
3.修改类的属性或方法:在类创建时动态添加或修改类的方法。
4.实现单例模式:通过元类实现类的单例行为。
type()
type 是最常见的元类。我们可以用它动态地创建类,也可以定义自己的元类来定制类的行为。
用 type 动态创建类:
def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
h = Hello()
h.hello()
# 输出:
Hello, world.
要创建一个class对象,type()函数依次传入3个参数:
1.class的名称;
2.继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
3.class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
metaclass
metaclass 是用于定义和控制类创建过程的特殊类。先定义metaclass,就可以创建类,最后创建实例。
# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMetaclass):
pass