Python 是一种支持面向对象编程(Object-Oriented Programming, OOP)的语言,可以很容易在Python中创建一个类和对象。
面向对象编程是一种编程范式,它使用"对象"来设计应用和软件。在面向对象的程序中,数据(属性)和功能(方法)被封装在对象中,并通过对象间的交互来实现程序的功能。
一、面向对象的基本特征
- 类(Class): 用来描述具有相同的属性和方法的对象的
集合
。类是对象的蓝图或模板,它定义了对象的属性和方法
。 - 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
对象是类的实例
,具有类定义的属性和方法。 - 属性(Attribute):
属性是对象的数据部分
,它存储了对象的状态信息。 - 方法(Method):方法是对象的行为的定义,即
对象可以执行的操作
。即类中定义的函数。 - 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。
类变量通常不作为实例变量使用。 - 数据成员:类变量或者实例变量,用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,
这个过程叫方法的覆盖(override),也称为方法的重写。 - 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:在类的声明中,属性是用变量来表示的。
这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。 - 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。
继承也允许把一个派生类的对象作为一个基类对象对待。 - 实例化:创建一个类的实例,类的具体对象。
二、面向对象编程
1、类的使用
注意:需要在单独的文件中定义一个类。
类的一般语法:
python
class ClassName:
'类的帮助信息' #类文档字符串
class_suite #类体(由类成员、方法、数据属性组成)
示例1:
python
# 定义一个名为Dog的类
class Dog:
'基类'
dogCount = 0 #类变量
def __init__(self, name, age):
# 初始化方法,当创建类的新实例时自动调用
self.name = name # 对象的属性
self.age = age
Dog.dogCount += 1
def bark(self):
# 类的方法
print(f"{self.name} says Woof!")
def totaldogCount(self):
# 类的方法
print("Total Employee %d" % Dog.dogCount)
# 创建类的实例(对象)通过 __init__ 方法接收参数
my_dog = Dog("Buddy", 3)
# 访问对象的属性,使用.来访问属性
print(my_dog.name) # 输出:Buddy
print(my_dog.age) # 输出:3
# 调用对象的方法
my_dog.bark() # 输出:Buddy says Woof!
my_dog.totaldogCount()
运行结果:
python
Buddy
3
Buddy says Woof!
Total Employee 1
在这个示例中:定义了一个名为 Dog 的类
,它有一个初始化方法 init 和两个方法 bark和totaldogCount。创建了一个 Dog 类的实例 my_dog
,设置了它的属性 name 和 age
。访问了 my_dog 的属性并调用了它的方法
。
- dogCount 是
类变量
,其值将在这个类的所有实例之间共享
。可以在内部类或外部类使用 Dog.dogCount访问。 - 第一种方法
__init__()方法
是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。
self 代表类的实例
,self 在定义类的方法时是必须有的
,虽然在调用时不必传入相应的参数。
2、内置类属性
当创建一个类后,系统会自动为该类赋予一些内置类属性。这些属性名通常使用双下划线(__)包围,以与普通属性名区分。以下是一些常见的内置类属性:
-
init:类的初始化方法,当创建类的新实例时会自动调用。通常,我们在这里进行属性的初始化操作。__init__的第一个参数总是self,代表创建的实例本身。
-
new:一个静态方法,用于创建对象实例。它相当于构造器,负责对象的创建。__new__的第一个参数是cls,代表类本身。
-
str:一个特殊方法,用于定义当使用print函数或str()函数时对象的字符串表示形式。
-
doc:一个属性,用于存储对象的文档字符串。通过访问对象的__doc__属性,我们可以获取其文档字符串。
-
dict:这个属性可以作用在文件、类或类的对象上,最终返回的结果为一个字典,包含了对象或类的属性。
-
bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
-
还有其他一些内置类属性,如__file__、name 、module 、base、__bases__等,它们各自具有特定的用途和含义。
这些内置类属性都是Python语言本身提供的,用于支持面向对象编程的各种特性和功能。
在编写Python代码时,了解这些内置类属性的用法和含义,可以帮助我们更有效地利用Python的面向对象编程特性。
对示例1查看内置类属性:
python
print ("Dog.__doc__:", Dog.__doc__)
print ("Dog.__name__:", Dog.__name__)
print( "Dog.__module__:", Dog.__module__)
print ("Dog.__bases__:", Dog.__bases__)
print ("Dog.__dict__:", Dog.__dict__)
运行结果:
python
Dog.__doc__: None
Dog.__name__: Dog
Dog.__module__: __main__
Dog.__bases__: (<class 'object'>,)
Dog.__dict__: {'__module__': '__main__', 'dogCount': 1, '__init__': <function Dog.__init__ at 0x00000281DAB9B380>, 'bark': <function Dog.bark at 0x00000281DAB9B420>, 'totaldogCount': <function Dog.totaldogCount at 0x00000281DAB9B4C0>, 'prt': <function Dog.prt at 0x00000281DAB9B560>, '__dict__': <attribute '__dict__' of 'Dog' objects>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}
3、self代表类的实例,而非类
示例2;
python
class onetest:
def prt(self):
print(self)
print(self.__class__)
t = onetest()
t.prt()
class twotest:
def prt(too):
print(too)
print(too.__class__)
运行结果:
python
<__main__.onetest object at 0x000002A8CA8CF1D0>
<class '__main__.onetest'>
<__main__.twotest object at 0x000002A8CA8CF200>
<class '__main__.twotest'>
这个示例可知,self代表的是类的实例,代表当前对象的地址,而self.__class__则是指向类。self不是关键字,换成其他的too也可以执行。
4、对象销毁(垃圾回收)
Python 使用了引用计数来跟踪和回收垃圾。
在 Python 内部记录着所有使用中的对象各有多少引用。
一个内部跟踪变量,称为一个引用计数器。
当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。
Python 的垃圾收集器是一个引用计数器和一个循环垃圾收集器。
__del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行。
示例如下:
python
class demo:
def __init__(self,x=0,y=0,z=0):
self.x = x
self.y = y
self.z = z
def add(self):
self.z = self.x + self.y
print(self.z)
return self.z
def __del__(self):
class_name = self.__class__.__name__
print(class_name, "销毁")
# 创建类的对象
dem = demo(1,2,5)
#访问对象的属性
print(dem.x,dem.y,dem.z)
#调用对象的函数or方法
dem.add()
# 再次创建类的对象
dem1 = demo()
dem2=dem1
dem3=dem2
print(id(dem1),id(dem2),id(dem3))
运行结果:
python
1 2 5
3
1602445688368 1602445688368 1602445688368
demo 销毁
demo 销毁
5、类的继承
类的继承允许创建一个新类(称为子类或派生类),继承自一个或多个已存在的类(称为父类或基类)。通过继承,子类可以自动获得父类的属性和方法,并可以添加或覆盖自己的属性和方法。这提供了一种重用代码和组织代码层次结构的强大机制
。
示例:
python
# 定义一个父类
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement this method")
# 定义一个子类,继承自Animal类
class Dog(Animal):
def __init__(self, name, breed):
# 调用父类的初始化方法
super().__init__(name)
self.breed = breed
def speak(self):
# 实现父类中未实现的方法
return f"{self.name} barks!"
# 创建一个Dog类的实例
my_dog = Dog("小黄", "Labrador")
# 访问实例的属性
print(my_dog.name) # 输出: 小黄
print(my_dog.breed) # 输出: Labrador
# 调用实例的方法
print(my_dog.speak()) # 输出: 小黄 barks!
运行结果:
python
小黄
Labrador
小黄 barks!
在示例中,Dog 类继承
了 Animal 类。
Dog 类通过调用 super().__init__(name)
来调用父类 Animal 的 init 方法,以初始化从 Animal 继承的属性 name。且Dog 类添加了自己的属性
breed,并实现了 speak 方法。
super() 函数是调用父类方法
的一种常见方式。super() 返回一个临时对象,它绑定到父类,并允许调用父类的方法。这种方式特别有用当类继承自多个父类,且需要明确调用特定的父类方法时。
类的继承可以形成复杂的层次结构,一个类可以继承自多个父类(多重继承
),但通常应该谨慎使用,以避免复杂的继承和复杂的依赖关系。在Python中,多重继承是允许的,但可能会导致一些难以预料的行为,特别是在处理方法解析顺序(MRO,Method Resolution Order)时。
注意点:
(1) 若子类需要覆盖
父类的方法(即使用自己的实现替代父类的实现),可以简单地在子类中定义同名方法
。当在子类的实例上调用该方法时,Python 会优先使用子类中的定义
。
示例:
python
class Parent: # 定义父类
def myMethod(self):
print('调用父类方法')
class Child(Parent): # 定义子类
def myMethod(self):
print('调用子类方法')
# 创建子类实例
dem = Child()
dem.myMethod() # 子类调用重写方法
运行结果:
python
调用子类方法
(2) 若父类中的方法不应该被子类直接覆盖,或者需要在子类实现之前执行一些操作,可以在父类中使用 raise NotImplementedError
来抛出一个异常。这样,若子类没有实现该方法
,当尝试调用时就会触发异常。