面向对象编程
面向对象
常见的三种编程范式
函数式编程
函数可以作为参数传递、修改,或作为返回值
函数内不修改外部变量的状态面向过程编程(函数)
根据操作数据的语句块来实现功能。- 面向对象编程(类)
把数据和功能结合起来,用称为对象的东西包裹起来组织程序的方法。
面向对象概念
对象
对象:通过类定义的数据结构实例。
对象包括两个数据成员(类变量和实例变量)和方法。
python一切皆对象,类是对象,实例也是对象
实例
实例:具体的某个事物,某个类型实实在在的一个例子。
实例化:创建一个类的实例,类的具体对象。
类是一种生产实例的工厂。
属性
对象的描述信息,一种状态 => 变量
空调的属性: 品牌、功率、节能等级
人的属性:身高、体重、姓名、性别、爱好
方法
对象的行为 => 类中定义的函数
空调的方法: 制冷、制热
类的定义
类名的规范 : 一般首字母大写(大驼峰),如: Person, GoodsInfo
函数的命名: 由小写字母和下划线组成,如: scan_book, drink
类的使用
用关键字class进行定义,有三种定义格式,一般使用第三种!
class Bus317:
pass
class Bus317():
pass
class Bus317(object):
pass
__init__方法:
__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,它在类的一个对象被建立
后,自动运行。实例化参数与__init__函数个数一致
class Bus317(object):
prod = "BYD"
def __init__(self, busid):
self.busid = busid
def run(self, flag):
if flag:
print("从农大到长华小区")
else:
print("从长华小区到农大")
bus1 = Bus317("000001")
bus2 = Bus317("000002 ")
__new__方法
__new__:创建一个实例
这个名称的开始和结尾都是双下划线.
这个方法用来创建一个实例,一般情况下都不会重写。
注意事项:/_/init__与//new__执行顺序:先执行//new__,再执行//init__
//new__必须产生和class类型一致的instance, 否则//_init__是不会执行的
# 创建
# cls => 类;self => 实例
def __new__(cls, *args, **kwargs):
print("创建实例")
print(*args, **kwargs)
# 创建并返回一个实例
instance = super().__new__(cls)
print("创建的实例为",instance)
# instance.__init__(*args, **kwargs)
return instance
什么情况下重写__new__
设计模式:单例模式 => 只能创建一个实例
# 类属性instance = None def __new__(cls, *args, **kwargs): # 用一个变量记录是否已创建实例 if not instance: instance = super().__new__(cls) return instance
- 当创建实例的时候,需要做另外一些事情….(__init__)
用类实现斐波拉基序列
class Fib(object):
def __init__(self,n):
self.prevent = 0
self.current = 1
self.n = n
self.count = 0
def __next__(self):
#输出前前n项
if self.count == self.n:
raise StopIteration("没得了!")
self.count += 1
ret = self.current
self.current ,self.prevent = self.prevent + self.current, self.current
return ret
def __iter__(self):
return self
from collections.abc import Iterator
fib = Fib(50)
print(isinstance(fib,Iterator))
for i in fib:
print(i)
类变量与实例变量
类的变量: 由一个类的所有对象(实例)共享使用。 => 大家共用的属性,如国籍(省内存)=> 14亿人
实例的变量: 由类的每个对象/实例拥有。
类和实例都有自己的存储空间
所有的这些对象都有命名空间,而继承就是由下而上,从左到右原则搜索此树,来寻找属性名称所 出现的最低的地方。
实例方法与类方法
类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
继承完全可以理解成类之间的类型和子类型关系。
可以节省很多的代码,不需要写,直接使用
衍生出不同的子类,大部分代码一样,部分代码不一样
class Animal(object):
def __init__(self):
print(self)
print("Animal init")
def breath(self):
print("呼吸作用")
def say(self):
print("可以同类交流")
# 可以继承自多个父类
class Person(Animal):
def __init__(self):
print("Person init")
# 重写父类的方法
def say(self):
print("可以说好多种语言")
def work(self):
print("学习~~")
class Pig(Animal):
def say(self):
print("oink")
子类有__ini__: 执行自己的初始化函数
子类没有__init__: 在子类实例化的时候会自动执行父类的构造函数
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法父类里的方法
类的多态
多态(Polymorphism)按字面的意思就是“多种状态” 。在面向对象语言中,接口的多种不同的 实现方式即为多态。
类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个 whoAmI() 方法:
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def whoAmI(self):
return 'I am a Person, my name is %s' % self.name
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
def whoAmI(self):
return 'I am a Student, my name is %s' % self.name
class Teacher(Person):
def __init__(self, name, gender, course):
super(Teacher, self).__init__(name, gender)
self.course = course
def whoAmI(self):
return 'I am a Teacher, my name is %s' % self.name
在一个函数中,如果我们接收一个变量 x,则无论该 x 是 Person、Student还是 Teacher,都可以正确打印出结果:
def who_am_i(x):
print x.whoAmI()
p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')
who_am_i(p)
who_am_i(s)
who_am_i(t)
运行结果:
I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice
这种行为称为多态。也就是说,方法调用将作用在 x 的实际类型上。s 是Student类型,它实际上拥有自己的 whoAmI()方法以及从 Person继承的 whoAmI方法,但调用 s.whoAmI()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。
Python中的多态- 注意
• 多态以继承和重写父类方法为前提
• 多态是调用方法的技巧,不会影响到类的内部设计
新式类与经典类
- 在Python 2.x及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位 置),都属于“新式类”
- 不由任意内置类型派生出的类,则称之为“经典类” 。
- “新式类”和“经典类”的区分在Python 3.x之后就已经不存在,在Python 3.x之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类” 。
- 建议使用新式类
经典类与新式类在类型上的区别
• 经典类
- 所有的类都是classobj类型,而类的实例都是instance类型。
- 类与实例只有通过class属性进行关联
• 新式类
- 类实例的类型是这个实例所创建自的类(通常是和类实例的class相同)
简单来说就是有没有继承object
经典类与新式类的继承顺序 : Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
不同版本间存在差异:
- py2:经典类是按深度优先来继承的,新式类是按C3 算法来继承的
- py3:统一按C3算法来继承的
静态方法、类方法、实例方法
普通方法(使用实例中的数据)
静态方法(无需使用实例封装的内容@staticmethod)
类方法(会自动加当前类的类名 @classmethod)
各种方法的区别(@staticmethod、@classmethod)
实例方法不能通过类名调用,但是静态方法和类方法是可以(不实例化访问)
实例方法:可以通过self访问实例属性
类方法:可以通过cls访问类属性(希望取的值不受实例影响时使用)
静态方法:不可以访问,通过传值的方式
属性包装(@propert)
- 把函数包装成属性,使用的时候用对象名.属性名
- Person类中有一个属性为age,控制age的范围只能在0~150之间
属性包装的应用
property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检
查,这样,程序运行时就减少了出错的可能性。class Person(object):
def _init__(self): self._age = None @property def age(self): print("当访问是执行这里") return self._age @age.setter def age(self,n): print("当设置时执行这里") if 0<=n<=100: self._age = n else: print("您给的值不被允许")
p1 = Person()
p1.age = 4
#结果
“””
当设置时执行这里
当访问是执行这里
4
当设置时执行这里
您给的值不被允许
“””
python 类中的下划线
标识符是用来标识某种对象的名称。
在命名标识符时,需要遵循一定规则。标识符的第一个字符必须是字母(大小写均可),或者是一个下划 线(“_”)。
以下划线开头的标识符是有特殊意义的
以单下划线开头的(_foo)
- 类:这类成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量
- 模块:如果你写了代码“from <模块/包名> import *”,那么以“_”开头的模块和包都不会被导
入,除非模块或包中的“all”列表显式地包含了模块和包。 - 这有点类似于惯例,为了使其他人(或你自己)使用这些代码时将会知道以“”开头的名称只供内*部
使用。正如Python文档中所述:以下划线“”为前缀的名称(如_spam)应该被视为API中非公开的
部分(不管是函数、方法还是数据成员)。此时,应该将它们看作是一种实现细节,在修改它们时无
需对外部通知。
以双下划线开头的(__foo)
- 类:只有类对象自己能访问,连子类对象也不能访问到这个数据。强行访问“对象名._类名
__xxx“这样的方式 - 模块:不能用“from xxx import *“导入包/模块。
- 名称(具体为一个方法名)前双下划线(““)的用法并不是一种惯例,对解释器来说它有特定
的意义。Python中的这种用法是为了避免与子类定义的名称冲突。Python文档指出,
“spam” 这种形式(至少两个前导下划线,最多一个后续下划线)的任何标识符将会被“_classname__spam”这种形式原文取代,在这里“classname”是去掉前导下划线的当前类
名。
以双下划线开头和结尾的( __foo__ )
• 代表Python中特殊方法专用的标识。其实,这只是一种惯例,对Python系统来说,这将确保不会 与用户自定义的名称冲突。
• 例如__init__()代表类的构造函数。
• 虽然你也可以编写自己的特殊方法名,但不建议用户使用这种命名方式。
魔法函数
常用魔术方法和属性
构造函数(__new__/__init__)
• __new__:创建实例
• __init__:初始化实例
• 如果不定义,python会使用默认的__init__
析构函数(__del__)
• 在实例释放、销毁的时候自动执行的,通常用于做一些收尾工作, 如关闭一些数据库连接,关闭
打开的临时文件
• 如果不定义,python会使用默认的__del__
调用方法(__call__)
• 把类实例化后的对象当做函数来调用的时候自动被调用
获取数据(__getitem__)
• a[key]来访问对象,a[1:3]切片时调用__getitem__
删除数据(__delitem__)
• del r[1] 时调用__delitem__方法
设置数据(__setitem__)
• r[‘e’]=213时调用__setitem__
获取可迭代(__iter__)