Python中的面向对象编程
背景:
最近在看一些代码的时候,对类中的一些内置方法不是很懂,因此出一篇文章来细说一下,希望大家看完后对Python中类有一个清楚的认识。
基础铺垫:
面向对象的三个特点:封装、继承、多态。面向对象的好处无非就是增加代码的复用性,利于维护和修改,这也是高内聚,低耦合的体现。
- 封装:
- 封装是一种将数据(属性)和操作数据的方法(方法)封装在一个单元内的机制。
- 类的成员变量可以设置为私有,只能通过类的方法来访问和修改。
- 继承:
- 继承允许你创建一个新类,该类继承了一个现有类的属性和方法。新类称为子类,原始类称为父类或基类。
- 子类可以扩展或修改继承的属性和方法,也可以添加新的属性和方法。
- 多态:
- 多态性允许不同类的对象对相同的方法名做出不同的响应。这是通过方法的重写和接口的实现来实现的。
类的特性:
Python使用class关键字来定义类,其基本结构如下:
-
class 类名(): #一般类名首字母是大写 pass
内置方法合集(重点):
内置方法(也称为魔术方法或双下划线方法),它们具有特殊的含义和用途,为什么你有的时候看不懂一些方法,因为他是固定的,比较便捷,我们只需要对其重写即可。
__init__(self, ...)
: 这是一个类的构造方法,用于初始化对象的属性。当你创建一个类的新实例时,__init__
方法会自动调用,进行相关的赋值操作。__str__(self)
: 用于返回一个可读的对象字符串表示。当你使用print
函数打印一个对象时,它会自动调用__str__
方法来获取字符串表示,我们一般对其重写。__repr__(self)
: 用于返回一个对象的官方字符串表示。通常,它应该返回一个字符串,以用于创建相同对象的副本。__len__(self)
: 这用于返回对象的长度。你可以通过内置函数len()
来获取对象的长度,它会自动调用__len__
方法。__getitem__(self, key)
: 这用于允许对象像字典或列表一样通过索引或键来访问其元素。它用于实现对象的索引访问。__setitem__(self, key, value)
: 用于允许对象像字典或列表一样通过索引或键来设置其元素的值。它用于实现对象的索引赋值。__delitem__(self, key)
: 用于允许对象像字典或列表一样通过索引或键来删除其元素。它用于实现对象的索引删除。
class Book:
# self 是调用者
def __init__(self, title, author, pages): # Book类内置属性 标题 作者 页数
self.title = title
self.author = author
self.pages = pages
def __str__(self):
return f"{self.title} by {self.author}"
def __repr__(self):
return f"Book({self.title}, {self.author}, {self.pages})"
def __len__(self):
return self.pages
def __getitem__(self, page_number):
if page_number >= 1 and page_number <= self.pages:
return f"Page {page_number} of {self.title}"
else:
raise IndexError("Page number out of range")
def __iter__(self):
self.current_page = 1 # 封装一个属性
return self
def __next__(self):
if self.current_page <= self.pages:
result = f"Page {self.current_page} of {self.title}"
self.current_page += 1
return result
else:
raise StopIteration
# 创建一个Book对象
book = Book("Python Basics", "John Smith", 100) #会自动调用 __init__ 方法
# 使用内置方法
# 打印对象 会自动调用__str__
print(book) # 输出: Python Basics by John Smith
# 调用__len__ 函数
print(len(book)) #输出 100
# 调用__repr__ 函数
print(repr(book)) # 输出: Book(Python Basics, John Smith, 100)
# __getitem__ 当取某一个元素得时候会自动调用
print(book[1]) # 输出: Page 1 of Python Basics
print(book[50]) # 输出: Page 50 of Python Basics
# # 迭代书的页面
for page in book:
print(page)
# 第一次调用会执行__iter__函数,然后不断使用__next__ 函数,for page in book:会反复调用 __next__ 方法,每次迭代都会获取下一页的页面信息,直到没有更多的页面可供迭代为止。
组合:
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
class Student():
def __init__(self):
# 将创建好的手机对象赋值给了phone这个实例变量
self.phone = Phone('霸王别姬')
class Phone():
def __init__(self, movie_name):
self.movie_name = movie_name
def playMovie(self):
print('手机正在播放的电影是:', self.movie_name)
s1 = Student()
s1.phone.playMovie()
继承:
通过继承,你可以创建一个新类(子类),它可以继承另一个类(父类或基类)的属性和方法。子类可以扩展或修改父类的功能,并可以添加自己的属性和方法。
- 父类和子类:
- 父类是被继承的类,也被称为基类或超类。
- 子类是继承父类的类,也被称为派生类。
- 继承语法:
- 在子类的类定义中,将父类作为子类的参数传递给类定义。
- 使用
super()
函数可以在子类中调用父类的方法。
class ParentClass:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} is speaking.")
class ChildClass(ParentClass):
def __init__(self, name, age):
super().__init__(name) # 调用父类的构造方法
self.age = age
def speak(self):
super().speak() # 调用父类的方法
print(f"{self.name} is {self.age} years old and speaking.")
child = ChildClass("Alice", 10)
child.speak()
class ParentClass:
def __init__(self, name):
self.name = name
class ChildClass(ParentClass):
def __init__(self, name, age):
# 不显式调用父类的构造方法,Python会自动调用
self.age = age
child = ChildClass("Alice", 10)
print(child.name) # 输出: Alice
print(child.age) # 输出: 10
子类 ChildClass
的构造方法没有显式调用 super().__init__(name)
,但仍然可以正确地初始化 name
属性,因为Python会自动调用父类 ParentClass
的构造方法。但是,如果你在子类的构造方法中想做一些其他特定于子类的初始化工作,你可以显式调用 super().__init__(name)
来确保父类的构造方法也被执行。
多态:
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
def make_animal_speak(animal):
return animal.speak()
dog = Dog()
cat = Cat()
print(make_animal_speak(dog)) # 输出: "Woof!" 调用谁得对象,执行who得函数
print(make_animal_speak(cat)) # 输出: "Meow!"
实例变量和类变量:
实例变量:
- 实例变量指的是实例化对象本身拥有的变量。
- 通过实例名加圆点的方式调用实例变量 对象.属性
class Student():
def __init__(self,i_name,i_age):
#只要定义在init方法内部的变量就是【实例/对象变量】
self.name = i_name #self.name就是定义的实例变量,name是init方法的参数值
self.age = i_age #self.age就是定义的实例变量,age就是init方法的参数值
s1 = Student('xxx',21) #调用Student类中的init这个构造方法
s2 = Student('lisi',225)
print(s1.name,s1.age) #访问s1对象的name和age这两个实例变量
print(s2.name,s2.age) #访问s2对象的name和age这两个实例变量
类变量:
顾名思义,类和实例化对象公用得属性叫做类变量。定义在类中,方法之外的变量,称作类变量。类变量是所有实例公有的变量,每一个实例都可以访问类变量。
class Student():
# 定义在方法外部的变量:类变量
address = 'Beijing'
classroom = 167
def __init__(self, i_name, i_age):
# 只要定义在init方法内部的变量就是【实例/对象变量】
self.name = i_name
self.age = i_age
s1 = Student('zhangsan', 20) # 调用Student类中的init这个构造方法
s2 = Student('lisi', 25)
# 根据对象的引用访问对象的实例变量
print(s1.name, s1.age) # 访问s1对象的name和age这两个实例变量
print(s2.name, s2.age) # 访问s2对象的name和age这两个实例变量
print(s1.address, s1.classroom) # 对象访问类变量
print(Student.address,Student.classroom) # 类访问类变量
一句话:类变量是可以被所有的对象公用的
类的方法:
Python的类中可以包含三种不同类型的方法:实例方法、静态方法和类方法。它们之间的区别主要涉及参数和调用方式,
实例方法:
- 实例方法是最常见的方法类型,在类内部定义时,第一个参数通常是
self
,它表示对象自身。 - 实例方法可以访问和修改对象的属性,因为它们有对当前实例的引用。
class Student():
classroot = 167 #类变量
#构造方法
def __init__(self,name,age):
#实例变量
self.name = name
self.age = age
#注意:实例方法只可以通过对象调用。
def study(self,book):
print('正在学习的书籍是:',book)
s = Student('zhangsan',20) #实例化对象
#只给除了self其他的参数传值
s.study('C++程序设计')
静态方法:
- 静态方法在类内部定义时,使用
@staticmethod
装饰器来标识,它们不需要访问对象的状态,因此没有self
参数。 - 静态方法通常用于类级别的操作,而不是实例级别的操作。
class Obj():
def __init__(self):
pass
# 定义一个静态方法
@staticmethod
def staticFunc(name): # 静态方法不需要有任何的必要参数(self)
print('我是静态方法!,我有一个普通参数:', name)
Obj.staticFunc('帅哥') # 通过类名调用(推荐)
o = Obj()
o.staticFunc('小帅哥') # 通过对象名调用(不推荐)
类方法:
- 类方法在类内部定义时,使用
@classmethod
装饰器来标识,它们的第一个参数通常是cls
,它表示类本身。 - 类方法可以访问和修改类级别的属性,通常用于创建、操作或修改类级别的状态。
class Obj():
f = 'classVar' # 类变量
def __init__(self):
pass
@classmethod
def classFunc(cls): # 类方法必须要有一个cls的参数,且作为第一个参数
# cls也不是python的关键字,cls也可以写作其他的形式,比如:xx,self
print('我是类方法!必要参数cls的值为:', cls)
print('类变量的值为:', cls.f) # 类名访问类变量
o = Obj()
o.classFunc() # 通过对象名访问(不推荐)
Obj.classFunc() # 通过类名访问(推荐)