面向对象第一课:类与对象
面向对象刚开始是让开发者自己定义数据类型,其中有两个核心:一个是类型(简称类),另一个是对象(实例),像我们之前学习的,
类型 | 类型名称 | 对象 |
整数 | int | -1,1,2,100 |
浮点数 | float | 3.14,10.1,-2.5 |
None类型 | NoneType | None |
布尔 | bool | True,bool |
字符串 | str | "hello world","good",... |
列表 | list | [1,2,3],['age','height','gender'] |
元组 | tuple | {1,2,3} |
集合 | set | {1,2,3} |
字典 | dict | {'x':10,'y':100} |
看了这么多,那到底类与对象是什么呢?打个比喻说吧:
比如说,类就像汽车的设计图纸,对象就像在街上跑着承担交通运输任务的车辆。,利用Python提供的类型作为基石,创造出我们自己的类型,而Python为我们提供了这样的语法攻击
在写类时,我们要先写类的名称,类名要遵循大驼峰命名法,所谓大驼峰命名法就是混合使用大小写类的名字,当类的名字由一个或多个单词连在一块组成唯一的名称时,每个单词的首字母都要采用大写字母,这样可以增加程序的可读性。
编写类需要三步:
一:声明类,也就是把表的第一行翻译成python语言
二:声明属性:把表中的属性翻译成代码,注意,属性必须要有默认值
用类名加括号就可以创建一个对象,创建对象又称实例化对象
比如:创建矩形对象:
print(Rectangle())#打印的是对象在堆中的地址
把创建出的变量赋值给一个变量:
rect = Rectangle()
然后使用变量名.属性来访问属性的值
print(rect.length,rect.width)
修改对象属性的值使用变量名.属性=值即可。
rect= Rectangle()
rect.length = 5
rect.width = 2
print(rect.length,rect.width)
整理为一个函数:
rect= Rectangle()
rect.length = 5
rect.width = 2
rect1= Rectangle()
rect1.length = 4
rect1.width = 3
def area(rect): #参数接受一个长方形对象
returnrect.length *rect.width
print(area(rect) ) #计算长方形对象
print(area(rect2))
三:表示行为特征的函数要写在类中,常被称为方法
调用类中的方法
class Rectangle:
length = 0
width = 0
def area(rect):
return rect.length *rect.width
def perimeter(rect): return 2 * (rect.length +rect.width)
rect = Rectangle()
rect.length = 3
rect.width = 4
print(rect.area())
print(rect.perimeter())
魔法方法
魔法方法的名字是固定的,一般情况下我们不需要通过明显的方式调用他们,但他们会在特殊阶段自动执行。
__init__()
__str__()
__init__()函数,可以在实例化对象的时候修改属性的值
class Rectangle:
length = 0
width = 0
def __init__(self,length,width):
self.length =length
self.width =width
def area(rect):
returnrect.length *rect.width
def perimeter(rect):
return 2 * (rect.length +rect.width)
魔法函数要写在类里面,所以init函数至少有一个参数self,通过参数要给修改的属性传参数,一般参数名和属性名一致,一旦写了__init__函数,并且__init__函数除了self以外还有其他参数,就需要为__init__函数除了self以外的参数传入值。也可以给__init__函数的参数设置默认值。
__str__函数,该函数要求返回一个字符串,这个字符串的信息要能够把对象的信息组织起来,当我们使用print函数打印对象的时候,实际上打印的就是__str__函数返回的字符串。
class Rectangle:
length = 0
width = 0
def __init__(self,length=0,width=0):
self.length =length
self.width =width
def area(rect):
returnrect.length *rect.width
def perimeter(rect):
return 2 * (rect.length +rect.width
def __str__(self):
return '长方形的长是' + str(self.length) + ',宽是' + str(self.width)
rect = Rectangle(3, 4)
print(rect)
封装
公有成员是可以公开使用的,即可以在类的内部进行访问,也可以在外部程序中使用。Python中类的成员函数、成员变量默认都是公开的。
私有成员在类的外部不能直接访问,一般在类的内部进行访问和操作,或者在类的外部通过调用对象的公有成员方法来访问,这是类的封装特性的重要体现。
__xxx:以两个下划线开头,表示类的私有成员,一般只有类对象自己能访问,子类对象也不能直接访问该成员,但可以通过“对象名._类名__xxx”这样的特殊方式来访问。
class Point:
x = 10
__y = 20
z = 30
def get_x(self):
returnself.x
def get_y(self):
return self.__y
def __get_z(self):
returnself.z
继承
单继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承用于指定一个类将从其父类获取其大部分或全部功能。它是面向对象编程的一个特征。这是一个非常强大的功能,方便用户对现有类进行几个或多个修改来创建一个新的类。新类称为子类或派生类,从其继承属性的主类称为基类或父类。子类或派生类继承父类的功能,向其添加新功能。它有助于代码的可重用性。
class 派生类(基类):
派生类类内语句
只需要继承基类这个动作,就可以访问到基类的属性和方法了,它提高了代码的可扩展性。
特点:
•如果派生类中有__init__()方法,那么基类的__init__()方法不会被自动调用,它需要在其派生类的构造中专门调用。
•如果派生类中没有__init__()方法,且基类中有__init__()方法,那么基类的__init__()方法会被自动调用
•调用父类的方法有三种:
方法1:父类名字.父类中的方法名(self,[参数1,参数2,参数3,……])
方法2:super().父类中的方法名([参数1,参数2,参数3,……])
方法3:super(当前类的名字,self).父类中的方法名([参数1,参数2,参数3,……])
•Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。
任何事情都有利有弊:继承的一个弱点就是,可能特殊的类又有其他特殊的地方,又会定义一个类,其下也可能再定义类,这样就会造成继承的那条线越来越长,使用继承的话,任何一点小的变化也需要重新定义一个类,很容易引起类的爆炸式增长,产生一大堆有着细微不同的子类。所以有个“多用组合少用继承”的原则。
多继承
Python子类可以继承一个基类,也可以继承多个基类,这就是多重继承。类的多重继承的语法如下。
class 派生类(基类1,基类2,...):
派生类类内语句
在Python3.x版本其寻找方法遵循MRO原则,可以通过类.__mro__属性输出对应的方法寻找顺序。注意:__mro__属性显示指定类的所有继承脉络和继承顺序,假如这个指定的类不具有某些方法和属性,但与其有血统关系的类中具有这些属性和方法,则在访问这个类本身不具有的这些方法和属性时,会按照__mro__显示出来的顺序一层一层向后查找,直到找到为止。
多态
多态是指基类的同一方法在不同派生类对象中具有不同的变现和行为。不同的派生类对象调用相同的基类方法,产生了不同的执行结果,这样可以增加代码的外部调用灵活度,多态以继承和重写父类方法为前提条件,多态只是调用方法的技巧,不会影响到类的内部设计。
话不多说,上图:
#没有使用多态
class ArmyDog(object):
def bite_enemy(self):
print("追击敌人。")
class DrugDog(object):
def track_drug(self):
print("追查毒品。")
class Person(object):
def work_with_army(self,dog):
dog.bite_enemy()
def work_with_drug(self,dog):
dog.track_drug()
person = Person()
person.work_with_army(ArmyDog())
person.work_with_drug(DrugDog())
#使用多态
class Dog:
def work(self):
pass
class ArmyDog(Dog):
def work(self):
print("追击敌人。")
class DrugDog(Dog):
def work(self):
print("追查毒品。")
class Person: # 只要能接收父类对象,就能接收子类对象
def work_with_dog(self,dog): # 只要父类对象能工作,子类对象就能工作。并且不同子类会产生不同的执行效果。
dog.work()
person = Person()
person.work_with_dog(ArmyDog())
person.work_with_dog(DrugDog())