Python学习(十二) —— 面向对象
一、初识面向对象
面向过程的核心是过程,流水线思维,过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点:极大地降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点:一套流水线或流程就是用来解决一个问题,代码牵一发而动全身。
面向对象的程序设计的核心是对象,上帝式思维,要理解何为对象。
优点:解决了程序的扩展性,使程序的扩展和维护变得简单,并且大大提高程序开发效率,基于面向对象的程序可以使他人更加容易理解你的代码逻辑。
缺点:可控性差,无法像面向过程的程序设计流水线式的可以很精确地预测问题的处理流程和结果。
二、类和对象
类:具有相同属性和方法的一类事物
对象:具体的拥有准确属性的一些变量
实例化:从一个类创造一个具体的对象的过程
类的两个作用:属性引用和实例化对象
class Person: role = 'person' def walk(self): print('person in walking') print(Person.role) #查看role属性 print(Person.walk) #引用人的走路方法,注意,不是调用
实例化:类名加上括号就是实例化,会自动出发__init__函数的运行
class Person: role = 'person' def __init__(self,name,age,sex): self.name = name self.age = age self.sex =sex def walk(self): print('person in walking') alex = Person('alex',18,'男') #实例化对象 print(alex.name) #查看对象属性 alex.walk() #调用对象的方法
类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
三、类的命名空间和对象的命名空间
创建一个类就会创建一个类的命名空间,用来存储类中定义的所有名字,这些名字称为类的属性
类有两种属性:静态属性和动态属性。静态属性是在类中定义的变量,动态属性是在类中定义的方法
创建一个对象就会创建一个对象的命名空间,存放对象的名字,称为对象的属性
在obj.name会先从自己的命名空间找name,找不到则去类中找,类也找不到就找父类,最后找不到就抛出异常
修改变量:如果是可变数据类型中的元素进行修改,那么全局生效
如果是对变量进行重新赋值,那么只是在对象自己的命名空间里增加了一个新的属性
#设计一个类,统计这个类被实例化的次数,且所有对象共享这个属性 class Foo: count = 0 def __init__(self): Foo.count += 1 f1 = Foo() print(f1.count) #1 f2 = Foo() print(f2.count) #2 f3 = Foo() print(f3.count) #3
四、面向对象的组合用法
在一个类中以另一个类的对象作为数据属性,称为类的组合。
将一个类的对象拥有的属性,再将其定义成一个类以提高代码的复用性。
计算圆环的周长面积,定义一个圆类,计算圆的的周长和面积,再定义一个圆环类,将外圆和内圆的周长和面积传入,得到圆环的周长和面积
from math import pi class Circle: #定义一个圆形类 def __init__(self,r): self.radius = r def perimeter(self): return 2*pi*self.radius #计算周长 def area(self): return pi*(self.radius**2) #计算面积 class Ring: #定义一个圆环类 def __init__(self,outer_r,inner_r): self.outer_circle = Circle(outer_r) #把圆形类的对象当作圆环类的属性 self.inner_circle = Circle(inner_r) def perimeter(self): return self.outer_circle.perimeter() + self.inner_circle.perimeter() def area(self): return self.outer_circle.area() - self.inner_circle.area()
五、面向对象的三大特性
1.继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
python中的继承分为:单继承和多继承
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #多继承,用逗号分隔开 pass
查看继承:__base__只查看从左到右的继承的第一个父类,__bases__查看所有继承的父类,并以元组方式显示
如果没有指定基类,python的类会默认继承Object类,Object是python所有类的基类
继承和抽象:先抽象再继承
抽象最主要的功能是划分类别,继承是基于抽象的结果,通过编程语言去实现。
子类继承父类你使用父类的属性和方法
class Animal: #定义一个父类 def __init__(self,name,aggr,life_value): #定义父类属性 self.name = name self.aggr = aggr self.life_value = life_value def eat(self): #当以父类方法 self.life_value += 100 print('%s add life_value'%self.name) class Person(Animal): #继承父类 def __init__(self,name,aggr,life_value,money): Animal.__init__(self,name,aggr,life_value) #调用父类属性 self.money = money def eat(self): print('%s is eating'%self.name) class Dog(Animal): def __init__(self,name,aggr,breed,life_value): #调用父类属性 Animal.__init__(self,name,aggr,life_value) self.breed = breed james = Person('james',50,250,100) flower = Dog('flower',99,'hashiqi',500) print(james.life_value) #调用父类属性 james.eat() #如果方法重名,调用子类方法 flower.eat() #调用父类方法 print(flower.life_value) Animal.eat(james) #调用父类方法
class Animal: def __init__(self,name,aggr,life_value): self.name = name self.aggr = aggr self.life_value = life_value def eat(self): self.life_value += 100 print('%s add life_value'%self.name) class Person(Animal): def __init__(self,name,aggr,life_value,money): Animal.__init__(self,name,aggr,life_value) self.money = money def eat(self): print('%s is eating'%self.name) Animal.eat(self) #调用父类的方法 james = Person('james',50,250,100) james.eat() #同时调用子类和父类的方法
派生属性:父类没有的属性
派生方法:父类没有的方法
经典类使用父类的方法:父类名.方法名(子类对象) 例如:Animal.eat(snoopy)
新式类使用父类的方法:super(子类名,self).父类方法名(),例如:super(Dog,self).eat()
class Animal: def __init__(self,name,aggr,life_value): self.name = name self.aggr = aggr self.life_value = life_value def eat(self): self.life_value += 100 print('%s add life_value'%self.name) class Person(Animal): def __init__(self,name,aggr,life_value,money): super(Person,self).__init__(name,aggr,life_value) #调用父类属性 self.money = money def eat(self): print('%s is eating'%self.name) super(Person,self).eat() #调用父类的方法 class Dog(Animal): def __init__(self,name,aggr,breed,life_value): super(Dog,self).__init__(name,aggr,life_value) #调用父类属性 self.breed = breed james = Person('james',50,250,100) flower = Dog('flower',99,'hashiqi',500) print(james.life_value) #调用父类属性 james.eat() #同时调用子类和父类的方法
钻石继承问题
经典类:深度优先
新式类:广度优先,查看继承顺序:模块名.mro()
接口类:是规范子类的一个模板,接口类一般只定义方法,不实现功能,只要接口类中定义的,就应该出现在子类中
接口类不能被实例化,只能被继承
支持多继承
java不支持多继承,用Interface关键字来声名接口
from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): #模板,接口类 @abstractmethod #装饰接口类中方法的,加上这个装饰器,自动检测子类中的方法名 def pay(self,money):pass #定义函数不实习功能 class Apple_Pay(Payment): #继承接口类 def pay(self,money): print('您使用苹果支付支付了%s元'%money) #实现功能 class Ali_Pay(Payment): def pay(self, money): print('您使用支付宝支付了%s元' % money) class WeChat_Pay(Payment): def pay(self,money): print('您使用微信支付了%s元' % money) def pay(obj,money): return obj.pay(money) #多态,根据传入对象的不同,自动调用不同类的方法 apple = Apple_Pay() pay(apple,100) #您使用苹果支付支付了100元
抽象类:抽象类可以实现子类一些共有的功能和属性,抽象类不鼓励多继承
抽象类不能被实例化
这个抽象类可以规范子类必须实现抽象类的抽象方法
from abc import ABCMeta,abstractmethod #导入模块的方法 class Base(classmethod=ABCMeta): #定义一个抽象类 def __init__(self,filename): self.filename =filename @abstractmethod def open(self): #定义方法,可以实现功能 return 'file handler' @abstractmethod #定义多个方法 def close(self):pass @abstractmethod def read(self):pass @abstractmethod def write(self):pass class File(Base): #继承抽象类,抽象类的所有方法都要继承 def open(self):pass def close(self):pass def read(self):pass def write(self):pass
2.多态
多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
class Person(): def attack(self): print('person attack dog') class Dog(): def attack(self): print('dog attack person') def attack(obj): #多态,根据传入不同的类对象执行不同的类方法 obj.attack() person = Person() dog = Dog() attack(person) #person attack dog attack(dog) #dog attack person
3.封装
封装:将一些方法和属性放到类里,这本身就是一种封装。
把属性和方法藏在类里,只能在类内部调用,不能在外部使用。
定义一个私有属性\方法:__名字
在类外部不能直接使用,如果一定要用,在私有方法之前加上:_类名__名字
在类外的名字,可以通过__dict__就可以查看
class Dog: __role = 'dog' #私有的静态属性 def __discount(self): #私有的方法 print('in func') def price(self): self.__discount() print(Dog.__dict__) print(Dog._Dog__role)