面向对象
1.面向对象的简述
Python是一种面向对象的语言,这意味着Python程序可以由对象组成。面向对象编程(OOP)是一种编程范式,它通过将程序视为一系列对象的集合来组织代码。这些对象包含数据和可以对这些数据执行操作的方法。
在Python中,面向对象编程涉及以下主要概念:
- 类(Class):类是创建对象的蓝图或模板。类定义了对象的属性和行为。
- 对象(Object):对象是类的实例。类定义了对象的结构和行为,而对象则是类的具体实例。
- 属性(Attribute):属性是对象的数据部分。每个对象都有其属性,这些属性描述了该对象的状态。
- 方法(Method):方法是附加到类或对象上的函数。方法是对象的行为,用于在运行时修改或查询对象的属性。
- 继承(Inheritance):继承是一种机制,通过它可以从一个类派生出另一个类。子类继承了父类的所有属性和方法,还可以定义自己的新属性和方法。
- 封装(Encapsulation):封装指的是隐藏对象的内部状态并仅通过对象的方法进行访问的能力。这提供了数据安全和防止直接访问内部数据结构的能力。
- 多态性(Polymorphism):多态性是指子类可以根据需要重写父类的方法。这意味着在调用一个方法时,将根据对象的实际类型来决定执行哪个版本的方法。
- 抽象(Abstraction):抽象是指只显示对象的重要信息而隐藏其细节的能力。这通常通过定义抽象类和接口来实现。
是不是很懵逼,别急,下面我们会逐个的进行详细的介绍。
2.面向对象的优点
面向对象编程(OOP)具有许多优点,这些优点在软件开发中非常重要,包括:
- 代码的可重用性:通过使用类和对象,可以创建可重用的代码。如果一个类已经定义了,那么你可以在程序中的任何地方创建它的实例,无需重复编写相同的代码。
- 代码的可维护性:面向对象编程使得代码更容易理解和维护。对象和类的概念让代码更加模块化,可以独立于其他部分的代码进行修改。这降低了代码之间的相互依赖性,使得代码更容易理解和维护。
- 更好的代码结构:面向对象编程鼓励开发者将问题分解成一系列的对象和交互。这使得代码更加模块化和结构化,更容易处理复杂的问题。
- 更好的抽象能力:面向对象编程提供了抽象的机制,让开发者可以定义抽象类和接口,以及实现这些接口的具体类。这使得开发者可以更好地理解和处理现实世界中的复杂问题。
- 更好的封装性:面向对象编程提供了封装的能力,让开发者可以将数据和处理数据的方法捆绑在一起,形成对象。这增强了代码的安全性和可读性,同时也隐藏了对象的内部实现细节。
- 更好的可扩展性:面向对象编程提供了多态的机制,让开发者可以在不改变现有代码的情况下增加新的行为。这使得软件更容易扩展和适应新的需求。
- 更好的可测试性:面向对象编程的模块化特性使得各个模块更容易进行单元测试,提高了代码的测试性和可靠性。
3.类与对象
类与对象的定义上文已经提到了,话不多说,直接上例子进行解释说明
例1:学校,学生,张三
在这个例子中如果我们把学校看成一个类,那么学生就是对象;如果把学生看成一个类,那么张三就是一个对象
一般形式如下:
class 类名: # 类名的命名一般遵循驼峰命名法
pass
对象名 = 类名() # 对象也被称为“实例”
把学生看成一个类,张三是一个对象:
class Student:
def __init__(self, name): # __ 是两个下划线
self.name = name
print(f'我叫{self.name}')
s = Student('张三')
# 输出
我叫张三
__init()__
方法称为初始化方法,也可称为构造方法。在创建对象时,会自动执行该方法,为对象的属性设置初始值。
简单来说,用 def
定义的,就叫做方法。方法使用 self
作为第一个参数,它代表类的实例。这意味着方法可以访问和修改类的属性。 self
是一个形参,在实例化之后就是对象本身。
还是很懵,再来个例子?
# 定义一个猫类
class Cat:
# 初始化方法,默认被调用
def __init__(self, name, color):
# 实例属性
self.name = name
self.color = color
# 实例变量
c = Cat('肥波', '灰色')
print(c.name)
print(c.color)
# 输出
肥波
灰色
4.私有方法和私有属性
在面向对象编程中,私有方法和私有属性是类的一部分,但它们不能从类的外部直接访问。这种限制有助于保护对象的状态和行为,防止直接修改可能导致错误或不可预测的行为。
定义方法:在属性和方法名前加两个下划线(__)
- 私有属性
class Cat:
def __init__(self, name, color,age)
self.name = name
self.color = color
self.__age = age # 私有属性
c = Cat('肥波', '灰色',3)
print(c.name)
print(c.color)
print(c.__age)
# 输出
AttributeError: 'Cat' object has no attribute '__age'
这是因为 __age 为私有属性,外部成员无法访问私有属性。
如果想要访问私有属性需要在内部进行访问
class Cat:
def __init__(self, name, color, age):
self.name = name
self.color = color
self.__age = age
def get_age(self):
print(self.__age)
c = Cat('肥波', '灰色', 3)
print(c.name)
print(c.color)
c.get_age()
# 输出
肥波
灰色
3
- 私有方法
如果方法私有呢?方法中的代码还能执行吗?
class Cat:
def __init__(self, name, color, age):
self.name = name
self.color = color
self.age = age
def __sleep(self):
print(f'{self.name}在睡觉')
c = Cat('肥波', '灰色', 3)
print(c.name)
print(c.color)
c.__sleep()
# 输出
AttributeError: 'Cat' object has no attribute '__sleep'
运行之后会报错。那要怎么才能读取到私有方法中的代码呢?
class Cat:
def __init__(self, name, color, age):
self.name = name
self.color = color
self.age = age
def __sleep(self):
print(f'{self.name}在睡觉')
def get_sleep(self):
# 调用类里面的方法:类型.方法名
Cat.__sleep(self)
c = Cat('肥波', '灰色', 3)
print(c.name)
print(c.color)
c.get_sleep()
# 输出
肥波
灰色
肥波在睡觉
5.魔法方法
魔法方法是指一组特殊的方法,以双下划线(__)作为前缀和后缀,用于自定义对象的行为和交互方式。这些方法提供了一种方式,让开发者在编写类时,对Python的内置行为进行扩展或修改。
class Cat:
def __init__(self, name, color, age):
self.name = name
self.color = color
self.age = age
c = Cat('肥波', '灰色', 3)
print(c)
# 输出
<__main__.Cat object at 0x018D38F0>
class Cat:
def __init__(self, name, color, age):
self.name = name
self.color = color
self.age = age
def __str__(self):
return self.name
c = Cat('肥波', '灰色', 3)
print(c)
# 输出
肥波
观察两段代码有什么差别,第一段只是输出了对象的地址信息,而第二段输出 return 后面的值。这是因为下面的代码比上面的多了一个方法,这个方法就是魔法方法。
下面整理了一下魔法方法:
+ object.__add__(self, other)
- object.__sub__(self, other)
* object.__mul__(self, other)
/ object.__div__(self, other)
% object.__mod__(self, other)
& object.__and__(self, other)
| object.__or__(self, other)
+= object.__iadd__(self, other)
-= object.__isub__(self, other)
*= object.__imul__(self, other)
%= object.__imod__(self, other)
< object.__lt__(self, other)
== object.__eq__(self, other)
!= object.__ne__(self, other)
> object.__gt__(self, other)
str() object.__str__(self)
len() object.__len__(self)
dir() object.__dir__(self)
下面列举几个例子:
class Number:
def __init__(self, num):
self.num = num
self.l = []
def __add__(self, other):
return self.num + other.num
def __gt__(self, other):
if self.num > other.num:
return True
else:
return False
def add(self, x):
self.l.append(x)
print(self.l)
def __len__(self):
return len(self.l)
n1 = Number(3)
n2 = Number(2)
print(n1 + n2)
print(n1 > n2)
l1 = Number(n1)
l1.add(2)
print(len(l1))
# 输出
5
True
[2]
1
5.继承和多态
- 继承
继承格式:
class Father: # 父类 超类
pass
class Son(Father): # 子类 派生类
pass
单继承:子类继承父类,则可以直接享受父类中已经封装好的方法
class Animal:
def run(self):
print('在跑。。。。。')
# 继承了 Animal
class Dog(Animal):
def eat(self):
print('在吃。。。。。')
# 继承了 Dog
class Cat(Dog):
def sleep(self):
print('在睡觉。。。。。')
c = Cat()
c.sleep()
# 子类继承了父类,调用父类的方法
c.eat()
c.run()
# 输出
在睡觉。。。。。
在吃。。。。。
在跑。。。。。
在继承中还有一个重要的参数,那就是 super() ,下面来详细的介绍一下。
super()用来调用父类(基类)的方法。
super().init() 就是调用父类的init方法, 同样可以使用super()去调用父类的其他方法
# super() 父类和子类中没有相同的方法名时
class Animal:
def sleep(self):
print('A 在睡觉')
class Cat(Animal):
def eat(self):
# 猫类没有 sleep ,调用父类中的方法
self.sleep()
# 直接调用父类的方法
super().sleep()
print('在吃饭')
c = Cat()
c.eat()
# 输出
A 在睡觉
A 在睡觉
在吃饭
# super() 父类和子类中有同名的方法
class Animal:
def sleep(self):
print('A 在睡觉')
class Cat(Animal):
def sleep(self):
print('C 在睡觉')
def eat(self):
# 调用自己的 sleep
self.sleep()
# 调用父类的 sleep
super().sleep()
print('C 在吃饭')
c = Cat()
c.sleep()
c.eat()
# super().__init__()
class Animal:
def __init__(self):
print('A 在睡觉')
class Cat(Animal):
def __init__(self):
# 调用父类的构造方法
super().__init__()
print('C 在睡觉')
c = Cat()
# 输出
A 在睡觉
C 在睡觉
- 多态
动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
class Animal:
def func(self):
print('发出了叫声')
class Cat(Animal):
def func(self):
print('喵')
class Dog(Animal):
def func(self):
print('汪')
class Tiger(Animal):
def func(self):
print('嗷呜')
class Car:
def func(self):
print('这个是汽车类的方法,不是动物类的对象')
def work(musen):
musen.func()
work(Car())
# 输出
这个是汽车类的方法,不是动物类的对象
学完了面向对象,来个例题吧
创建一个学校成员类,登记成员名称并统计总人数。教师类与学生类分别继承学校成员类,登记教师所带班级与学生成绩,每创建一个对象学校总人数加一,删除一个对象则减一。
class SchoolMember:
sum_member = 0
def __init__(self, name):
self.name = name
SchoolMember.sum_member += 1
print("学校新加入一个成员:%s" % self.name)
print("现在有成员%d人" % SchoolMember.sum_member)
def say_hello(self):
print("大家好,我叫:%s" % self.name)
def __del__(self):
SchoolMember.sum_member -= 1
print("%s离开了,学校还有%d人" % (self.name, SchoolMember.sum_member))
class Teacher(SchoolMember):
def __init__(self, name, sclass):
SchoolMember.__init__(self, name)
self.sclass = sclass
def say_hello(self):
SchoolMember.say_hello(self)
print("我是老师,我带的班级是:%d" % self.sclass)
def __del__(self):
SchoolMember.__del__(self)
class Student(SchoolMember):
def __init__(self, name, mark):
SchoolMember.__init__(self, name)
self.mark = mark
def say_hello(self):
SchoolMember.say_hello(self)
print("我是学生,我的成绩是: %d" % self.mark)
def __del__(self):
SchoolMember.__del__(self)
t = Teacher("张伟", 1)
t.say_hello()
s = Student("李四", 99)
s.say_hello()
# 输出
学校新加入一个成员:张伟
现在有成员1人
大家好,我叫:张伟
我是老师,我带的班级是:1
学校新加入一个成员:李四
现在有成员2人
大家好,我叫:李四
我是学生,我的成绩是: 99
张伟离开了,学校还有1人
李四离开了,学校还有0人