1.初识面向对象
1.1 面向过程
面向过程:根据业务逻辑从上到下写代码。开发过程的思路是将数据与函数按照执行的逻辑顺序组织在一起,数据与函数分开考虑,面向过程基本是由函数组成的。
特点:
注重步骤与过程,不注重职责分工
如果需求复杂,代码会变得很复杂
开发复杂项目,没有固定的套路,开发难度很大!
1.2 面向对象
特点:
注重 对象和职责,不同的对象承担不同的职责。
更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路。
需要在面向过程基础上,再学习一些面向对象的语法。
2. 类和对象
2.1 类
类是对一群具有相同特征或者行为 的事物的一个统称,是抽象的,不能直接使用。类里包括属性和方法。所谓属性就是该类的特征,而方法就是该类可以执行的动作。
类的定义:
class 类名: 类名首字母大写
2.1.1 属性
python支持动态添加属性,谁创建对象,self就指向谁。属性可以是系统类型,也可以是自定义的类型。
类属性:直接在该类下定义的属性,即 变量名 = 值。
设置类属性:类名.属性名 = 值, 如果有该属性就修改,没有就新建该属性。
只要修改类属性,其余为未修改对象属性的对象的该属性都将改变。
类属性可以通过类对象和实例对象访问。
尽量避免类属性和实例属性同名。如果有同名实例属性,实例对象会优先访问实例属性。
类属性只能通过类对象修改,不能通过实例对象修改。
类属性也可以设置为私有,前边添加两个下划线。
对象属性:
设置对象属性:对象名.属性名 = 值, 如果有该属性就修改,没有就报错。
只要一经修改对象属性,无论类属性如何改变,该对象的该属性都不会改变。
上述方法给对象添加属性,可能会出现问题。所以引入__init__方法.
私有属性只能在类体内部使用或者调用各种方法才能使用,外界不能直接通过 对象名。属性名 来使用。格式为,__属性名,python将私有属性改名为_类名__属性名来隐藏该属性,使外界无法访问。也可以通过 对象名._类名__属性名来访问私有属性,但是强烈不建议这样使用。
2.1.2 方法
哪个对象调用了方法,方法里的self指的就是谁。 通过 self.属性名 可以访问到这个对象的属性;通过 self.方法名() 可以调用这个对象的方法。
对象方法:只能通过对象来调用
类方法: 对象方法上加@classmethod装饰器,将self改为cls。
调用: 类名。方法名()
对象名。方法名()
静态方法:对象方法上加@staticmethod装饰器,无需加self、cls。不依赖类
调用:
类名。方法名()
对象名。方法名()
2.2 对象
对象是由类创建出来的一个具体存在,可以直接使用属性名。由哪一个类创建出来的 对象,就拥有在哪一个类中定义的属性和方法。只要创建对象,就为其分配一个内存空间。
创建对象的语法:
对象变量名 = 类名()
使用dir函数查看对象的所有方法:
格式:
print(dir(对象名))
2.3 类和对象的关系
类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象。
使用同一个类,能够创建出很多对象。
类中定义了什么属性和方法,对象中就有什么属性和方法。
不同对象对应的属性值也会不同。
3. 魔法方法
3.1 __init__方法
init()方法,在创建一个对象时默认被调用,不需要手动调用。在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对 init 方法进行改造。
注意:
__init__()方法在创建对象时,会默认被调用,不需要手动的调用这个方法。
__init__()方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好 的对象引用直接赋值给self
在类的内部,可以使用self来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法。
如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。
方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个 对象调用了实例方法。
3.2__ str__方法
__str__方法返回对象的描述信息,使用print()函数打印对象时,其实调用的就是这个对象的__str__方法。
3.3 __del__方法
当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法。
3.4__ new__方法
创建对象时调用该魔法方法,先于init调用。它在内存申请了一个空间,供对象使用。
3.5__mro__方法
主要用于在多继承时判断方法属性的调用顺序。
new、init的区别:
new方法需要一个返回值,返回创建的实例对象,init不需要返回值
new负责对象的创建,而init负责对象的初始化
new创建对象时调用,会返回当前对象的一个实例,而init是创建完对象后调用,对当前对象进行初始化
在类中new,init同时存在优先调用new
如果__new__返回一个对象的实例,会隐式调用__init__
4.面向对象的特点
4.1 封装
私有属性,通过定义共有的set和get方法来获取和修改私有属性,为封装。
通过@property实现封装:
可以简化对象属性的获取和赋值,通过 对象名。属性获取属性值,通过对象名。属性 = 新值 来设置属性。
封装步骤:
@property
def 属性名(self):
return self.__xxx
获取私有属性值: 对象名.属性 = 值
@属性名.setter
def 属性名(self,yyy):
if 条件:
self.__xxx = yyy
else:
代码块
修改私有属性:对象名.属性名 = 新值
4.2 继承
继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。继承描述的是多个类之间的所属关系。
子类只能继承父类非私有的属性和方法
当子类和父类有相同的方法或者属性,优先使用子类自己的。
在子类里一个方法调用了另一个方法,而此方法在子类和父类中都有,优先调用子类的方法。如果要使用父类的方法,使用super().方法名()。
继承的传递性:子类拥有父类以及父类的父类中封装的所有属性和方法。
重写:当父类中的方法不能满足子类的需求,需要自己定义一个与父类同名的方法。当调用该方法时,优先调用子类的方法。如果要使用父类的方法,使用super(子类名,self).父类方法名()。
子类要新加属性时,需要重新声明父类的属性,格式如下
class Worker(Person):
def __init__(self,id,name,salary,hours,money):
super(Worker,self).__init__(id,name,salary)
self.hours = hours
self.money = money
语法:
class 类名(父类名):
pass
多继承
格式:
class 子类名(父类名1,父类名2...)
pass
注意:
当多个父类中有同名的方法或者属性,使用该方法或者属性时优先使用先传入的父类,所以应该避免使用多继承。
可以使用__mro__查看在多继承时方法属性的调用顺序。
print(类名.__mro__)
新式类:继承object,python3.x都是新式类,优先使用广度优先搜索
4.3 多态
不同的子类调用相同的父类方法,产生不同的执行结果,可以增加代码的外部灵活度。多态是以继承和重写父类方法为前提的,它是一种调用方法的技巧,不会影响到类的内部设计。
5.练习
class Pet:
type = '宠物'
def __init__(self, name, age, color):
self.name = name
self.age = age
self.color = color
def __str__(self):
return '当前类型是{},宠物名{}'.format(self.type, self.name)
class Dog(Pet):
type = '狗'
def see_home(self):
print('我能看家')
class Cat(Pet):
type = '猫'
def catch_mouse(self):
print('我能抓老鼠')
class Petshop:
def __init__(self, name):
self.name = name
self.petlist = []
def add_pet(self, pet):
if isinstance(pet, Pet):
self.petlist.append(pet)
print('宠物添加成功')
else:
print('不是宠物,无法添加。')
def sale_pet(self, pet):
if isinstance(pet,Pet):
self.petlist.remove(pet)
print('宠物卖成功')
else:
print('不能卖非宠物')
def show_all(self):
print('宠物商店宠物信息:')
for pet in self.petlist:
print(pet)
def search_pet(self, pname):
for pet in self.petlist:
if pet.name == pname:
print('宠物在商店中')
break
else:
print('不存在该宠物!')
p = Petshop('华联宠物商店')
c = Cat('mao', 2, '白色')
d = Dog('gou',5,'黄色')
p.add_pet(c)
p.add_pet(d)
p.sale_pet(c)
p.show_all()