面向对象
编程范式
编程是 程序 员 用特定的语法+数据结构+算法 组成的代码来告诉计算机如何执行任务的过程 。
一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。 两种最重要的编程范式分别是面向过程编程和面向对象编程。
面向过程编程
面向过程:核心是过程二字,过程指的是解决问题的步骤,设计一条流水线,机械式的思维方式
优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象编程
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
面向对象的程序设计的
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
在python 中面向对象的程序设计并不是全部。
面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
了解一些名词:类、对象、实例、实例化
类:具有相同特征的一类事物(人、狗、老虎)
对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)
实例化:类——>对象的过程(这在生活中表现的不明显,我们在后面再慢慢解释)
在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。
定义类及类的操作
#先定义类
class LuffyStudent:
school='luffycity' #数据属性
def learn(self): #函数属性
print('is learning')
def eat(self): #函数属性
print('is sleeping')
# 查看类的名称空间
print(LuffyStudent.__dict__)
print(LuffyStudent.__dict__['school'])
print(LuffyStudent.__dict__['learn'])
# 查
print(LuffyStudent.school) #LuffyStudent.__dict__['school']
print(LuffyStudent.learn) #LuffyStudent.__dict__['learn']
#增
LuffyStudent.county='China'
print(LuffyStudent.__dict__)
print(LuffyStudent.county)
#删
del LuffyStudent.county
#改
LuffyStudent.school='Luffycity' # 复制修改
__init__方法
__init__方法用来为对象定制对象自己独有的特征
class LuffyStudent:
school='luffycity'
# stu1, '王二丫', '女', 18
def __init__(self,name,sex,age):
self.Name=name
self.Sex=sex
self.Age=age
#stu1.Name='王二丫'
#stu1.Sex='女'
#stu1.Age=18
def learn(self):
print('is learning')
def eat(self):
print('is sleeping')
# 后产生对象
stu1=LuffyStudent('王二丫','女',18) # LuffyStudent.__init__(stu1,'王二丫','女',18)
#加上__init__方法后,实例化的步骤
# 1、先产生一个空对象stu1
# 2、LuffyStudent.__init__(stu1,'王二丫','女',18)
查
print(stu1.__dict__)
#print(stu1.Name)
#print(stu1.Sex)
#print(stu1.Age)
改
stu1.Name='李二丫'
# print(stu1.__dict__)
# print(stu1.Name)
删除
del stu1.Name
# print(stu1.__dict__)
增
# stu1.class_name='python开发'
# print(stu1.__dict__)
# stu2=LuffyStudent('李三炮','男',38)
# Luffycity.__init__(stu2,'李三炮','男',38)
# print(stu2.__dict__)
# print(stu2.Name)
# print(stu2.Age)
# print(stu2.Sex)
类和对象的命名空间
# 类里 可以定义两种属性
# 静态属性
# 动态属性
class Course:
language = ['Chinese'] # 可变数据类型
def __init__(self,teacher,course_name,period,price):
self.teacher = teacher
self.name = course_name
self.period = period
self.price = price
def func(self):
pass
# Course.language = 'English'
# Course.__dict__['language'] = 'Chinese' # 不能这样修改
# print(Course.language)
python = Course('egon','python','6 months',20000)
linux = Course('oldboy','linux','6 months',20000)
#['chinese']
python.language = ''
# print(python.language)
# print(linux.language)
# Course.language = 'Chinese'
# print(python.language)
# print(linux.language)
# del python.language
# print(python.language)
# print(python.__dict__)
# print(Course.language)
# print(linux.language)
# print(linux.__dict__)
# 类中的静态变量 可以被对象和类调用
# 对于不可变数据类型来说,类变量最好用类名操作
# 对于可变数据类型来说,对象名的修改是共享的,重新赋值是独立的
模拟人生
class Person:
money = 0
def work(self):
Person.money += 1000
mother = Person()
father = Person()
Person.money += 1000
Person.money += 1000
print(Person.money)
mother.work()
father.work()
创建一个类,每实例化一个对象就计数
最终所有的对象共享这个数据
class Foo:
count = 0
def __init__(self):
Foo.count += 1
f1 = Foo()
f2 = Foo()
print(f1.count)
print(f2.count)
f3 = Foo()
print(f1.count)
# 认识绑定方法
# def func():pass
# print(func)
#
# class Foo:
# def func(self):
# print('func')
# def fun1(self):
# pass
# f1 = Foo()
# print(Foo.func)
# print(f1.func)
# print(f1.fun1)
#<bound method Foo.func of f1>
包 中的—— __init__
# import package —— 类的实例化的过程
# import time
# time.time()
类里的名字有 类变量(静态属性量)+ 方法名(动态属性)
对象里的名字 对象属性
对象 —— > 类
对象找名字 : 先找自己的 再找类的 找不到就报错
对象修改静态属性的值
对于不可变数据类型来说,类变量最好用类名操作
对于可变数据类型来说,对象名的修改是共享的,重新赋值是独立的
对象可以使用静态变量? True
类可以使用对象里的属性么? False
组合
一个类的对象是另外一个类对象的属性
是类与类之间什么有什么的关系
class Dog:
def __init__(self,name,aggr,hp,kind):
self.name = name
self.aggr = aggr
self.hp = hp
self.kind = kind
def bite(self, person):
person.hp -= self.aggr
class Person:
def __init__(self,name,aggr,hp,sex):
self.name = name
self.aggr = aggr
self.hp = hp
self.sex = sex
self.money = 0
def attack(self,dog):
dog.hp -= self.aggr
def get_weapon(self,weapon):
if self.money >= weapon.price:
self.money -= weapon.price
self.weapon = weapon
self.aggr += weapon.aggr
else:
print("余额不足,请先充值")
class Weapon:
def __init__(self,name,aggr,njd,price):
self.name = name
self.aggr = aggr
self.njd = njd
self.price = price
def hand18(self,person):
if self.njd > 0:
person.hp -= self.aggr * 2
self.njd -= 1
alex = Person('alex',0.5,100,'不详')
jin = Dog('金老板',100,500,'teddy')
w = Weapon('打狗棒',100,3,998)
# alex装备打狗棒
alex.money += 1000
alex.get_weapon(w)
print(alex.weapon)
print(alex.aggr)
alex.attack(jin)
print(jin.hp)
alex.weapon.hand18(jin)
print(jin.hp)
jin.bite(alex)
print(alex.hp)
# 组合 :一个对象的属性值是另外一个类的对象
# alex.weapon 是 Weapon类的对象
面向对象的三大特性
继承、多态、封装
继承
什么是继承?
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): # python支持多继承,用逗号分隔开多个继承的类
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
经典类与新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
>>> ParentClass1.__bases__
(<class 'object'>,)
>>> ParentClass2.__bases__
(<class 'object'>,)
继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
继承与重用性
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def move_forward(self):
print('%s move forward' %self.nickname)
def move_backward(self):
print('%s move backward' %self.nickname)
def move_left(self):
print('%s move forward' %self.nickname)
def move_right(self):
print('%s move forward' %self.nickname)
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class Garen(Hero):
pass
class Riven(Hero):
pass
g1=Garen('草丛伦',100,300)
r1=Riven('锐雯雯',57,200)
print(g1.life_value) #结果:300
r1.attack(g1)
print(g1.life_value) #结果:243
提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大节省了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.
再看属性查找
提示:像g1.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类。那么如何解释下面的打印结果呢?
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
b=Bar()
b.f2()
# 打印结果:
# Foo.f2
# Bar.f1
派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
class Riven(Hero):
camp='Noxus'
def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
print('from riven')
def fly(self): #在自己这里定义新的
print('%s is flying' %self.nickname)
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值
class Riven(Hero):
camp='Noxus'
def __init__(self,nickname,aggressivity,life_value,skin):
Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能
self.skin=skin #新属性
def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
Hero.attack(self,enemy) #调用功能
print('from riven')
def fly(self): #在自己这里定义新的
print('%s is flying' %self.nickname)
r1=Riven('锐雯雯',57,200,'比基尼')
r1.fly()
print(r1.skin)
'''
运行结果
锐雯雯 is flying
比基尼
'''
继承的实现原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>,
<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先
示范代码
class A(object):
def test(self):
print('from A')
class B(A):
def test(self):
print('from B')
class C(A):
def test(self):
print('from C')
class D(B):
def test(self):
print('from D')
class E(C):
def test(self):
print('from E')
class F(D,E):
# def test(self):
# print('from F')
pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类
在子类中调用父类的方法
在子类派生出的新方法中,往往需要重用父类的方法,我们有两种方式实现
方式一:指名道姓,即父类名.父类方法()
class Vehicle: #定义交通工具类
Country='China'
def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=power
def run(self):
print('开动啦...')
class Subway(Vehicle): #地铁
def __init__(self,name,speed,load,power,line):
Vehicle.__init__(self,name,speed,load,power)
self.line=line
def run(self):
print('地铁%s号线欢迎您' %self.line)
Vehicle.run(self)
line13=Subway('中国地铁','180m/s','1000人/箱','电',13)
line13.run()
方式二:super()
class Vehicle: #定义交通工具类
Country='China'
def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=power
def run(self):
print('开动啦...')
class Subway(Vehicle): #地铁
def __init__(self,name,speed,load,power,line):
#super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
super().__init__(name,speed,load,power)
self.line=line
def run(self):
print('地铁%s号线欢迎您' %self.line)
super(Subway,self).run()
class Mobike(Vehicle):#摩拜单车
pass
line13=Subway('中国地铁','180m/s','1000人/箱','电',13)
line13.run()
这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查找
A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
c=C()
c.test() #打印结果:from B
print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
接口类、抽象类#008078
抽象类和接口类:都是面向对象的开发规范,不可以实例化
抽象类:一种规范
一般情况下是单继承,能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现。
接口类:一种规范
原则:接口隔离原则,单一接口
默认多继承:由于功能比较复杂,所以不容易抽象出相同功能的具体实现写在父类中。
接口类
第一种情况,调用才报错
class Payment:
def pay(self, money):
raise NotImplemented # 子类中没有实现pay方法,抛出异常
class Wechat:
def pay(self, money):
print('已经使用微信支付了 %s 元' % money)
class Applepay(Payment):
def fuqian(self, money):
print('已经使用applepay支付了 %s 元' % money)
class Alipay:
def pay(self, money):
print('已经使用支付宝支付了 %s 元' % money)
def pay(pay_obj, money): # 统一支付入口
pay_obj.pay(money)
wechat = Wechat()
ali = Alipay()
apple = Applepay()
pay(wechat, 100)
pay(ali, 200)
pay(apple, 300) # 只有调用的时候才报错
第二种情况,实例化就报错
第二种 实例化就报错
from abc import abstractmethod,ABCMeta
class Payment(metaclass=ABCMeta): # 元类,定义一个规范的类
@abstractmethod # 要求子类中必须实现pay方法,否则实例化就报错
def pay(self, money):
pass # 子类中没有实现pay方法,抛出异常
class Wechat:
def pay(self, money):
print('已经使用微信支付了 %s 元' % money)
class Applepay(Payment):
def fuqian(self, money):
print('已经使用applepay支付了 %s 元' % money)
class Alipay(Payment):
def pay(self, money):
print('已经使用支付宝支付了 %s 元' % money)
def pay(pay_obj, money): # 统一支付入口
pay_obj.pay(money)
wechat = Wechat()
ali = Alipay()
apple = Applepay()
pay(wechat, 100)
pay(ali, 200)
pay(apple, 300)
接口类的多继承
接口类:满足接口隔离原则,面向对象开发的思想 规范
from abc import abstractmethod, ABCMeta
class Swim_Animal(metaclass=ABCMeta):
@abstractmethod
def swim(self):
pass
class Walk_animal(metaclass=ABCMeta):
@abstractmethod
def walk(self):
pass
class Fly_Animal(metaclass=ABCMeta):
@abstractmethod
def fly(self):
pass
class Tigger(Walk_animal, Swim_Animal): # 接口隔离原则
def walk(self):
pass
def swim(self):
pass
抽象类
也是一种编程规范,一般是单继承,实现的功能都是一样的时候使用
类 是从一堆对象中抽取相同的内容而来的,
抽象类 是从一堆类中抽取相同的内容而来的。
#一切皆文件
import abc #利用abc模块实现抽象类
class All_file(metaclass=abc.ABCMeta):
all_type='file'
@abc.abstractmethod #定义抽象方法,无需实现功能
def read(self):
'子类必须定义读功能'
with open('filaname') as f:
pass
@abc.abstractmethod #定义抽象方法,无需实现功能
def write(self):
'子类必须定义写功能'
pass
class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('文本数据的读取方法')
def write(self):
print('文本数据的读取方法')
class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('硬盘数据的读取方法')
def write(self):
print('硬盘数据的读取方法')
class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('进程数据的读取方法')
def write(self):
print('进程数据的读取方法')
wenbenwenjian=Txt()
yingpanwenjian=Sata()
jinchengwenjian=Process()
#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()
print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
总结
抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化
java :
java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题
python
python中原生没有接口类 :
python中自带多继承 所以我们直接用class来实现了接口类
python中支持抽象类 : 一般情况下是单继承,不能实例化
且可以实现python代码
多态
多态指的是一类事物有多种形态,比如
动物有多种形态:人,狗,猪
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): #动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')
文件有多种形态:文本文件,可执行文件
import abc
class File(metaclass=abc.ABCMeta): #同一类事物:文件
@abc.abstractmethod
def click(self):
pass
class Text(File): #文件的形态之一:文本文件
def click(self):
print('open file')
class ExeFile(File): #文件的形态之二:可执行文件
def click(self):
print('execute file')
多态性
python 天生支持多态
多态性是指在不考虑实例类型的情况下使用实例
为什么要用多态性(多态性的好处)
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
2.增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
>>> class Cat(Animal): #属于动物的另外一种形态:猫
... def talk(self):
... print('say miao')
...
>>> def func(animal): #对于使用者来说,自己的代码根本无需改动
... animal.talk()
...
>>> cat1=Cat() #实例出一只猫
>>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
say miao
'''
这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)
'''
python原生支持多态
python中不崇尚根据继承父类来得到相似
如果两个类刚好相似,并不产生父类的子类的兄弟关系,就是 鸭子类型
list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的
鸭子类型的优缺点:
优点 : 松耦合 每个相似的类之间都没有影响
缺点 : 太随意了,只能靠自觉
封装
广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种
只让自己的对象能调用自己类中的方法
狭义上的封装 —— 面向对象的三大特性之一
属性 和 方法都藏起来 不让你看见
class Person:
__key = 123 # 私有静态属性
def __init__(self,name,passwd):
self.name = name
self.__passwd = passwd 私有属性
def __get_pwd(self): 私有方法
return self.__passwd 只要在类的内部使用私有属性就自动的带上_类名
def login(self): 正常的方法调用私有的方法
self.__get_pwd()
alex = Person('alex','alex3714')
print(alex._Person__passwd) _类名__属性名
print(alex.login())
所有的私有 都是在变量的左边加上双下划綫
对象的私有属性
类中的私有方法
类中的静态私有属性
所有的私有的 都不能在类的外部使用
会用到私有的这个概念的场景
1.隐藏起一个属性 不想让类的外部调用
2.我想保护这个属性,不想让属性随意被改变
3.我想保护这个属性,不被子类继承
子类无法调用父类的任何私有属性和方法,即子类无法继承父类的私有属性和方法。
property
内置装饰器函数 只在面向对象中使用,将类中的方法伪装成属性,调用该方法的时候,不需要传参数。
from math import pi
class Circle:
def __init__(self,r):
self.r = r
@property
def perimeter(self): self后面不能有参数
return 2*pi*self.r
@property
def area(self):
return self.r**2*pi
c1 = Circle(5)
print(c1.area) # 圆的面积,实际是调用类中的方法
print(c1.perimeter) # 圆的周长
私有属性的查看、修改、删除
class Person:
def __init__(self,name):
self.__name = name
self.price = 20
@property
def name(self):
return self.__name
@name.deleter
def name(self):
del self.__name 删除
@name.setter
def name(self,new_name):
self.__name = new_name 修改 只能传一个参数
brother2 = Person('二哥')
brother2.name = 'newName' 修改属性名
del brother2.name 触发执行@name.deleter下的函数
print(brother2.name) # 查看
classmethod类方法、staticmethod静态方法
classmethod类方法
定义类调用的方法
当这个方法的操作只涉及静态属性的时候 就应该使用classmethod来装饰这个方法
class Goods:
__discount = 0.8
def __init__(self,name,price):
self.name = name
self.__price = price
@property
def price(self):
return self.__price * Goods.__discount
@classmethod # 把一个方法 变成一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象
def change_discount(cls,new_discount): 修改折扣,一个默认参数cls
cls.__discount = new_discount
apple = Goods('苹果',5)
print(apple.price) # ==>5
Goods.change_discount(0.5) #类调用
print(apple.price) # ==>2.5
staticmethod静态方法
在完全面向对象的程序中,如果一个函数 既和对象没有关系 也和类没有关系 那么就用staticmethod将这个函数变成一个静态方法
class Login:
def __init__(self,name,password):
self.name = name
self.pwd = password
def login(self):pass
@staticmethod
def get_usr_pwd(): # 静态方法
usr = input('用户名 :')
pwd = input('密码 :')
Login(usr,pwd)
Login.get_usr_pwd() # 直接调用
类方法和静态方法 都是类调用的
对象可以调用类方法和静态方法么? 可以 一般情况下 推荐用类名调用
类方法 有一个默认参数 cls 代表这个类 cls
静态方法 没有默认的参数 就象函数一样
内置函数
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
判断类与对象的关系
class Foo(object):
pass
obj = Foo()
print(isinstance(obj, Foo)) # True
issubclass(sub,super)检查sub类是否是 super 类的子类
判断子类与父类的关系
class Foo(object):
pass
class Bar(Foo):
pass
print(issubclass(Bar, Foo)) # True
反射
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
hasattr(object,name)
判断object中有没有一个name字符串对应的方法或属性,返回布尔值True、False
getattr(object, name, default=None)
"""
hasattr()
getattr()
setattr()
delattr()
"""
class A:pass
class B(A):pass
a = A()
print(isinstance(a,A))
print(issubclass(B,A))
print(issubclass(A,B))
反射 : 是用字符串类型的名字 去操作 变量
name = 1
eval('print(name)') # 安全隐患
反射 就没有安全问题
反射 : 是用字符串类型的名字 去操作 变量
反射对象中的属性和方法 # hasattr getattr setattr delattr
class A:
def func(self):
print('in func')
a = A()
a.name = 'alex'
a.age = 63
反射对象的属性
ret = getattr(a,'name') # 通过变量名的字符串形式取到的值
print(ret)
print(a.__dict__)
变量名 = input('>>>') # func
print(getattr(a,变量名))
print(a.__dict__[变量名])
反射对象的方法
a.func()
ret = getattr(a,'func')
ret()
class A:
price = 20
@classmethod
def func(cls):
print('in func')
反射类的属性
# A.price
print(getattr(A,'price'))
# 反射类的方法 :classmethod staticmethod
# A.func()
if hasattr(A,'func'):
getattr(A,'func')()
模块
import my
反射模块的属性
print(my.day)
print(getattr(my,'day'))
反射模块的方法
getattr(my,'wahaha')()
内置模块也能用
import time
print(getattr(time,'time')())
print(getattr(time,'asctime')())
def qqxing():
print('qqxing')
year = 2018
import sys
# print(sys.modules['__main__'].year)
# 反射自己模块中的变量
# print(getattr(sys.modules['__main__'],'year'))
# 反射自己模块中的函数
# getattr(sys.modules['__main__'],'qqxing')()
变量名 = input('>>>')
print(getattr(sys.modules[__name__],变量名))
要反射的函数有参数怎么办?
print(time.strftime('%Y-%m-%d %H:%M:S'))
print(getattr(time,'strftime')('%Y-%m-%d %H:%M:S'))
一个模块中的类能不能反射得到
import my
print(getattr(my,'C')())
if hasattr(my,'name'):
getattr(my,'name')
重要程度半颗星
setattr 设置修改变量
class A:
pass
a = A()
setattr(a,'name','nezha')
setattr(A,'name','alex')
print(A.name)
print(a.name)
delattr 删除一个变量
delattr(a,'name')
print(a.name)
delattr(A,'name')
print(a.name)
内置双下方法
双下__str__
双下__repr__
obj.__str__ str(obj)
obj.__repr__ repr(obj)
class A:
pass
a = A()
print(str(a)) # <__main__.A object at 0x000001F646A7D1D0>
a.__str__ --> object
object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址
class Teacher:
def __init__(self,name,salary):
self.name = name
self.salary = salary
def __str__(self):
return "Teacher's object :%s"%self.name
def __repr__(self):
return str(self.__dict__)
def func(self):
return 'wahaha'
nezha = Teacher('哪吒',250)
print(nezha) # 打印一个对象的时候,就是调用a.__str__ Teacher's object :哪吒
print(repr(nezha)) # {'name': '哪吒', 'salary': 250}
print('>>> %r'%nezha) # >>> {'name': '哪吒', 'salary': 250}
l = [1,2,3,4,5] # 实例化 实例化了一个列表类的对象
print(l)
%s str() 直接打印 实际上都是走的__str__
%r repr() 实际上都是走的__repr__
repr 是str的备胎,但str不能做repr的备胎
print(obj)/'%s'%obj/str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串
如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
repr(),只会找__repr__,如果没有找父类的
双下__len__
可以定制对象的len()方法
class Classes:
def __init__(self,name):
self.name = name
self.student = []
def __len__(self):
return len(self.student)
def __str__(self):
return 'classes'
py_s9= Classes('python全栈9期')
py_s9.student.append('二哥')
py_s9.student.append('泰哥')
print(len(py_s9)) len(对象)就是调用obj.__len__()方法
print(py_s9)
双下__del__
析构方法
class A:
def __del__(self): 析构函数: 在删除一个对象之前进行一些收尾工作
self.f.close()
a = A()
a.f = open() # 打开文件 第一 在操作系统中打开了一个文件 拿到了文件操作符存在了内存中
del a # a.f 拿到了文件操作符消失在了内存中
del a del 先执行了__del__这个方法,又删除了变量
引用计数
双下__call__
对象加()执行的是__call__方法
class A:
def __init__(self,name):
self.name = name
def __call__(self):
'''
打印这个对象中的所有属性
:return:
'''
for k in self.__dict__:
print(k,self.__dict__[k])
a = A('alex')()
item系列
双下__getitem__
双下__setitem__
双下__delitem__
通过字典的形式操作对象的属性
class Foo:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def __getitem__(self, item):
if hasattr(self,item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
del self.__dict__[key]
f = Foo('egon',38,'男')
print(f['name'])
f['hobby'] = '男'
print(f.hobby,f['hobby'])
del f.hobby # object 原生支持 __delattr__
del f['hobby'] # 通过自己实现的
print(f.__dict__)
双下__new__
构造方法,创建对象
class A:
def __init__(self):
self.x = 1
print('in init function')
def __new__(cls, *args, **kwargs):
print('in new function')
return object.__new__(A, *args, **kwargs)
a1 = A() 先执行__new__方法再执行__init__
单例模式
一个类 始终 只有 一个 实例
当你第一次实例化这个类的时候 就创建一个实例化的对象
当你之后再来实例化的时候 就用之前创建的对象
class A:
__instance = False
def __init__(self,name,age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
if cls.__instance:
return cls.__instance
cls.__instance = object.__new__(cls)
return cls.__instance
egon = A('egg',38)
egon.cloth = '小花袄'
nezha = A('nazha',25) # 原有对象相同的属性会被更新,新创建的对象没有的属性不会被覆盖
print(nezha)
print(egon)
print(nezha.name) # nezha
print(egon.name) # nezha
print(nezha.cloth) # '小花袄'
双下__eq__
obj == obj1执行的是__eq__方法,没有__eq__方法的时候, 默认的是比较两个对象的内存地址,__eq__方法可以定制
class A:
def __init__(self,name):
self.name = name
def __eq__(self, other):
if self.__dict__ == other.__dict__:
return True
else:
return False
ob1 = A('egon')
ob2 = A('egg')
print(ob1 == ob2) 执行的是__eq__方法
双下__hash__
hash(obj)执行的是对象的__hash__方法
class A:
def __init__(self,name,sex):
self.name = name
self.sex = sex
def __hash__(self):
return hash(self.name+self.sex)
a = A('egon','男')
b = A('egon','nv')
print(hash(a))
print(hash(b))
set
class A:
def __init__(self,name,sex,age):
self.name = name
self.sex = sex
self.age = age
def __eq__(self, other):
if self.name == other.name and self.sex == other.sex:
return True
return False
def __hash__(self):
return hash(self.name + self.sex)
a = A('egg','男',38)
b = A('egg','男',37)
print(set((a,b))) # unhashable
set 依赖对象的 hash eq