一、什么是面向对象
对象(Object)是内存中专门用来存储数据的一块区域。对象中可以存放各种数据,比如:数字、布尔值、代码等。对象由 对象的标识(id)、对象的类型(type)和 对象的值(value)三部分组成。
Python 是一门面向对象的编程语言。所谓的 面向对象 的语言,简单理解就是语言中所有操作都是通过对象来进行的。面向对象的编程语言,关注的是对象,而不关注过程。它将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。如果要使用某个功能,直接找到对应的对象即可。对于面向对象的语言来说,一切都是对象。面向对象 以 类 为单位,每种事物都具备自己的 属性 和 方法/功能。面向对象这种方式编写的代码,可读性比较高,并且易于维护,可复用性比较高。但是这种方式,不太符合常规的思维,编写起来稍微麻烦一点。
与之对应的就是 面向过程 的编程语言,面向过程的编程语言指将我们的程序的逻辑分解为一个一个的步骤,通过对每个步骤的抽象,来完成程序。但是这种编写的代码往往只适用于一个功能。如果要实现别的功能,即使功能相差极小,也往往需要重新编写代码,所以它的可复用性比较低,并且难以维护。面向过程 强调的是 功能行为,以 函数 为最小单位,考虑怎么做;这种编程方式,符合我们人类思维,编写起来相对简单。
面向过程其实是最为实际的一种思考方式,就算是面向对象的方法也是含有面向过程的思想。可以说面向过程是一种基础的方法。它考虑的是实际地执行。一般的面向过程是从上往下步步求精。面向对象主要是把事物给对象化,对象包括属性与行为。当程序规模不是很大的时候,面向过程的方法还会体现出一种优势。因为程序的流程很清楚,按着模块与函数的方法可以很好的组织。但对于复杂而庞大的系统来说,面向过程显着就很无力了。
二、类与对象
类(Class)和 对象(Object)是面向对象的核心概念。类 是对一类事物的描述,是 抽象的、概念上的定义,简单理解就相当于一个图纸;对象 是实际存在的该类事物的每个个体,是 具体的,因此也称为 实例(instance);在程序中我们根据类来创建对象。我们也称对象是类的实例。如果多个对象是通过一个类创建的,我们称这些对象是一类对象。
面向对象程序设计的重点是 类的设计,类的设计,其实就是 类的成员的设计;
- 创建类,设计类的内部成员(属性、方法)
- 创建类的对象
- 通过对象,调用其内部声明的属性或方法,完成相关的功能
class Person():passprint(Person)
print(id(Person))
print(type(Person), '\n')# 使用类创建对象
p1 = Person()
print(p1)
print(type(p1),'\n')p2 = Person()
p2.name = "Sakura"
print(p2.name,'\n')name = str("Sakura")
# 我们可以用isinstance()用来检查一个对象是否是一类类的实例
print(isinstance(p1, Person))
print(isinstance(p2, Person))
print(isinstance(name, Person))
类也是一个对象,类是创建对象的对象;
类是 type 类型的对象,定义类实际上就是定义了一个 type 类型的对象;
三、类的成员
3.1、属性
属性 用来描述具体某个对象的特征。描述的是对象的状态信息,通常以变量的形式进行定义。在类中我们定义的变量,将会成为所有实例的公共属性,所有实例都可以通过 对象.属性名 的方式访问这些变量。
直接在类中定义的属性是 类属性,类属性可以通过类或类的实例访问到,但是类属性只能通过类对象来修改,无法通过实例对象修改。我们还可以创建的实例中动态的添加属性,这种属性属于 实例属性。实例属性只能通过实例对象来访问和修改,类对象无法访问修改。
当我们调用一个对象时,解析器会先在当前对象中寻找是否含有该属性,如果有则直接返回当前对象的属性值,如果没有则去当前对象的类对象中去寻找,如果有则返回类对象的属性值,如果没有则报错。
class Person:# 类属性,直接在类中定义的属性name = "Unknown"# 类属性可以通过类或类的实例访问到
p1 = Person()
print("Person: ", Person.name)
print("p1", p1.name, '\n')# 类属性只能通过类对象来修改,无法通过实例对象修改
Person.name = "Sakura"
print("Person: ", Person.name)
print("p1", p1.name, '\n')# 通过实例对象添加的属性属于实例属性
# 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
p2 = Person()
p2.name = "Mikoto"
print("Person: ", Person.name)
print("p2: ", p2.name, '\n')del p2.name
print(p2.name)
类对象和实例对象都可以保存属性,如果这个属性是所有的实例共享的,则应该将其保存到类对象中。如果这个属性是某个实例独有的,则应该保存到实例对象中。一般情况下,属性保存在实例对象中。
class Person:# 类属性,直接在类中定义的属性name = "Unknown"p1 = Person()
p2 = Person()print(id(Person.name))
print(id(p1.name))
print(id(p2.name))
3.2、方法
方法 是类或对象行为特征的抽象,用来完成某个功能的操作。在其它编程语言中,方法 也被称为 函数 或 过程。在类中定义的函数,将会成为所有实例的公共方法,所有该类实例都可以通过 对象.方法名() 的形式调用方法。在实际开发中,我们可以将重复的代码、具有独立功能的代码抽取到方法中。使用方法后,我们可以提高代码的复用性和可维护性。
3.2.1、实例方法
实例方法 每次调用时,解析器都会自动传递第一个实参,这个参数就是调用方法的 对象本身,一般我们都会将这个参数命名为 self。实例方法通过对象调用时,会自动将当前对象作为 self 传入,实例方法通过类调用时,不会自动传递 self,必须我们手动传递 self。
class Person:# 实例方法每次调用时,解析器都会自动传递第一个实参# 实例方法的第一个参数就是调用方法的对象本身,一般我们都会将这个参数命名为selfdef say_hello(self):print(self)# 在方法中不能直接访问实例对象的属性print("你好,我是 %s" %self.name)p1 = Person()
print(p1,'\n')p1.name = "Sakura"
# 实例方法通过类调用时,会自动将当前对象作为self传入
p1.say_hello()
# 实例方法通过类调用时,不会自动传递self,必须我们手动传递self
Person.say_hello(p1)
print()p2 = Person()
print(p2,'\n')p2.name = "Mikoto"
p2.say_hello()
注意:方法调用时,解析器会把当前对象当作第一个参数自动传递,所以定义实例方法时,至少定义一个形参;
3.2.2、类方法
在类内部使用 @classmethod
来修饰的方法属于 类方法, 类方法的第一个参数就是当前的 类对象,一般我们都会将这个参数命名为 cls。类方法可以通过类调用,也可以通过实例调用。
class Person:# 类属性count = 0# 在类内部使用@classmethod来修饰的方法属于类方法# 类方法的第一个参数就是当前的类对象,一般我们都会将这个参数命名为cls@classmethoddef showInfo(cls):print(cls)print("我的身份编号为:",cls.count)p1 = Person()
# 类方法可以通过类调用
Person.showInfo()
# 类方法也可以通过实例调用
p1.showInfo()
注意:方法调用时,解析器会把当前类对象当作第一个参数自动传递,所以定义实例方法时,至少定义一个形参;
3.2.3、静态方法
在类中使用 @staticmethod
来修饰的方法属于 静态方法,静态方法不需要指定任何的默认参数。静态方法,基本上是一个和方法无关的方法,它只是保存到当前类中的函数,静态方法一般都是一些工具方法,和当前类无关。
class Person:# 在类中使用@staticmethod来修饰的方法属于静态方法# 静态方法不需要指定任何的默认参数@staticmethoddef show():print("我是一个人")p1 = Person()# 静态方法可以通过类或实例取调用
Person.show()
p1.show()
3.3、魔法属性
__doc__ # 查看类的描述信息
__module__ # 查看当前操作的对象在哪个模块
__class__ # 表示当前操作的对象的类是什么
__dict__ # 类或对象中的所有属性
class Person:"""人的描述类"""name = "unknown"def show_info(self):print(f"我是一个人,名字是: {self.name}")p = Person()
p.name = "Sakura"
p.age = 10print(f"p.doc: {p.__doc__}")
print(f"p.module: {p.__module__}")
print(f"p.class: {p.__class__}")
print(f"Person.dict: {Person.__dict__}")
print(f"p.dict: {p.__dict__}")
Python 解释器会将对象(类对象、实例对象)中的所有属性、方法,统统用字典存储,将属性的名字变为字典的 key,将属性对应的数据当做字典中的 value。将方法的名字变为字典中的 key,将方法所对应的引用当做字典中的 value;
3.4、魔法方法
在类中可以定义一些特殊方法(魔法方法),特殊方法都是以双下划线 __
开头,双下划线 __
结尾。特殊方法不需要我们自己调用,它会在特殊的时候自动调用。
__init__()
会在创建对象后立即执行,它可以用来向新创建的对象初始化属性。
object.__init__(self[, ...])
class Person:# __init__(self)会在对象创建后立即执行# __init__(self)可以用来向新创建的对象初始化属性def __init__(self, name, age):# 通过self向新创建堆额对象中初始化属性self.name = nameself.age = agedef showInfo(self):# 在方法中不能直接访问实例对象的属性print("你好,我是 %s,我今年 %d 岁。" %(self.name,self.age))# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
p1.showInfo()p2 = Person("Mikoto",14)
p2.showInfo()
__init()__
方法必须返回 None;
__new__()
这个特殊方法会在创建类时自动调用。
object.__new__(cls[, ...])
class Person:# __new__(cls)这个特殊方法会在创建类时自动调用def __new__(cls):print("__new__(cls)执行了")print(cls)# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person()
__del__()
这个特殊方法会在对象删除前调用。
object.__del__(self)
class Person:# __init__(self)会在对象创建后立即执行# __init__(self)可以用来向新创建的对象初始化属性def __init__(self, name, age):# 通过self向新创建堆额对象中初始化属性self.name = nameself.age = age# __del__(self)会在对象删除前调用def __del__(self):print("del(self)方法执行了")print(self.name,"被删除了")# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
p1 = Nonep2 = Person("Mikoto",14)
__call__()
会在对象加括号后自动执行。
object.__call__(self[, args...])
class Person:def __call__(self):print("call方法被执行了")# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person()
p1()
__str__()
会在当前对象转换为字符串时使用,它可以用来指定对象转换为字符串的结果。
object.__str__(self)
class Person:# __init__(self)会在对象创建后立即执行# __init__(self)可以用来向新创建的对象初始化属性def __init__(self, name, age):# 通过self向新创建堆额对象中初始化属性self.name = nameself.age = age# __str__(self)会在当前对象转换为字符串时使用# 它可以用来指定对象转换为字符串的结果def __str__(self):return "Person[name: %s, age %d]"%(self.name,self.age)# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
print(p1)
__repr__()
这个特殊方法会在当前对象使用 repr() 函数时调用,它的作用对象在‘交互模式’中直接输出的效果。
object.__repr__(self)
class Person:# __init__(self)会在对象创建后立即执行# __init__(self)可以用来向新创建的对象初始化属性def __init__(self, name, age):# 通过self向新创建堆额对象中初始化属性self.name = nameself.age = age# __repr__(self)这个特殊方法会在当前对象使用repr()函数时调用# 它的作用对象在‘交互模式’中直接输出的效果def __repr__(self):return "Person[name: %s, age %d]"%(self.name,self.age)# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
print(p1)
__getattribute__()
方法可以实现属性拦截的作用。
object.__getattribute__(self, name)
class Person:def __init__(self, name, age):self.name = nameself.age = agedef __getattribute__(self, attribute):if attribute == "age":return "直接问别人年龄是不礼貌的哦"else:return object.__getattribute__(self, attribute)p = Person("Sakura", 10)
print(f"name: {p.name}")
print(f"age: {p.age}")
如果我们想像操作字典的方式用键值对的方式操作对象,可以使用如下方法:
object.__getitem__(self, key) # 用键值对的方式获取属性
object.__setitem__(self, key, value) # 用键值对的方式设置属性
object.__delitem__(self, key) # 用键值对的方式删除属性
class Person:def __getitem__(self, key):print("__getitem()__方法执行了")def __setitem__(self, key, value):print("__setitem()__方法执行了")def __delitem__(self, key):print("__delitem()__方法执行了")p = Person()
p["name"] = "Sakura"
name = p["name"]
del p["name"]
有关比较的特殊方法。
object.__lt__(self, other) # 会在对象做小于(<)时调用,该方法的返回值会作为比较结果
object.__le__(self, other) # 会在对象做小于等于(<=)时调用,该方法的返回值会作为比较结果
object.__eq__(self, other) # 会在对象做等于(==)时调用,该方法的返回值会作为比较结果
object.__ne__(self, other) # 会在对象做不等于(!=)时调用,该方法的返回值会作为比较结果
object.__gt__(self, other) # 会在对象做大于(>)时调用,该方法的返回值会作为比较结果
object.__ge__(self, other) # 会在对象做大于等于(>=)时调用,该方法的返回值会作为比较结果
class Person:# __init__(self)会在对象创建后立即执行# __init__(self)可以用来向新创建的对象初始化属性def __init__(self, name, age):# 通过self向新创建堆额对象中初始化属性self.name = nameself.age = age# __gt__(self,other)会在对象做小于时调用,该方法的返回值会作为比较结果# 它需要两个参数,self表示当前对象,other表示和当前对象比较的对象def __gt__(self,other):return self.age > other.age# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
p2 = Person("Mikoto",15)
print(p1 > p2)
print(p2 > p1)
有关运算的特殊方法:
object.__add__(self, other)
object.__sub__(self, other)
object.__mul__(self, other)
object.__matmul__(self, other)
object.__truediv__(self, other)
object.__floordiv__(self, other)
object.__mod__(self, other)
object.__divmod__(self, other)
object.__pow__(self, other[, modulo])
object.__lshift__(self, other)
object.__rshift__(self, other)
object.__and__(self, other)
object.__xor__(self, other)
object.__or__(self, other)
object.__len__(self) # 获取对象的长度
object.__bool__(self) # 将当前对象个转换为布尔值
3.5、对象创建的流程
p1 = Person() 的运行流程:
- 创建一个变量;
- 在内存中创建一个空对象;
- 自动调用类中
__init__(self)
方法,然后将空对象已经调用类是括号内传入的的参数一同传给__init__()
方法; - 将对象的 id 赋值给变量;
class Person:# 类代码块中的代码只在类定义的时候执行一次print("我是Person中的代码块")def __init__(self):print("我是",self,"__init__(self)方法")p1 = Person()
p2 = Person()
类代码块中的代码只在类定义的时候执行一次;