python-面向对象
1.面向过程
-----什么叫做面向过程
面向过程:处理事物的步骤、流程。 核心:过程
优点:降低程序的复杂度 缺点:可扩展性差
2.面向对象
先记面向对象的三大特性:
1,封装 2,继承 3,多态(python中处处是多态,本身就带多态)
------面向对象:一切皆对象 核心:对象
优点:解决了程序可扩展性差的问题
注:在python中变量表示特征,由函数表示技能,因而类是变量与与函数的结合体,对象是变量与方法(指向类的函数)的结合体
3.类
类的概念:分类,共同特征与否判断一类还是不是一类,类是提取相同的特征
在现实生活中:先有对象再有类,在程序中则是先有类,再有对象
定义类:就是定义变量与函数 首字母大写
***由一个抽象的东西加括号后跟冒号就产生一个类,返回值叫实例化对象,实例化触发__init__执行,__init__方法不能有返回值,实例化尽量不要调用类属性
class Person: #定义一个人类 role='中国人' # 类属性 静态属性 def __init__(self,name,life_value,aggr): #__init__方法 动态属性 self.name=name #姓名 动态属性 self.life_value=life_value #生命值 动态属性 self.aggr=aggr #攻击力 动态属性 def attack(self,enemy): #定义了一个攻击的类属性方法 enemy.life_value=enemy.life_value-self.aggr #对象的生命值 print('攻击') egg=Person('egon',1000,50) #实例化这个对象 就等于执行Person.__init__() print(egg.name) #查看属性/调用方法 print(egg.life_value) print(egg.aggr)
实例化的过程就是类------对象的过程
self-----自动将实例化对象/实例本身传给__init__的第一个参数
程序之间的交互
class Person: role='中国人' # 类属性 静态属性 def __init__(self,name,life_value,aggr): #__init__方法 动态属性
#动态属性 self.name=name #姓名
self.life_value=life_value #生命值 self.aggr=aggr #攻击力 def attack(self,alex): #定义一个攻击的动态属性 alex.life_value-=self.aggr #alex的生命值 #egon.life_value-=self.aggr #egon的生命值
# 实例化对象 查看属性/调用方法 egon=Person('geon',1000,900) alex=Person('alex',500,100) print(egon.life_value) #egon未战斗之前的生命值 alex.attack(egon) #alex攻击egon print(egon.life_value) #egon战斗之后的生命值 print(alex.life_value) #alex未战斗之前的生命值 egon.attack(alex) #egon攻击alex print(alex.life_value) #alex战之后的生命值 class Dog: #定义狗类 def __init__(self,name,life_value,aggr): #__init__方法 动态属性
#动态属性 self.name=name #名字 self.life_value=life_value #生命值 self.aggr=aggr #攻击力 def bite(self,Person): #定义狗咬的技能 egon.life_value-=self.aggr #egon的被攻击后的生命值 旺财=Dog('小强',500,200) #实例化dog对象 # #egon与旺财大战 print(egon.life_value) #狗未攻击egon之前egon的生命值 旺财.bite(egon) #狗攻击egon print(egon.life_value) #egon被攻击之后的生命值 print(旺财.life_value) #egon未打狗之前狗的生命值 egon.attack(旺财) ##egon打狗 print(旺财.life_value) #狗被打之后的生命值 #alex与旺财大战 print(alex.life_value) #alex没被狗攻击之前的生命值 旺财.bite(alex) #狗攻击alex print(alex.life_value) #alex被咬之后的生命值 print(旺财.life_value) #狗没挨打之前的生命值 alex.attack(旺财) #alex打狗 print(旺财.life_value) #狗受伤之后的生命值
4.类命名空间、实例命名空间
创建一个类就会创建一类命名空间,用来存储类中定义所有名字,这些名字称为类名属性
而类有两种属性:静态属性和动态属性
- 静态属性就是直接在类中定义的变量
- 动态属性就是定义在类中的方法
# #面向对象名称空间问题 class Person:
role=‘中国人’
pass egon.role='印度人' print(egon.role,id(egon.role)) print(alex.role,id(alex.role)) print(Person.role,id(Person.role)) Person.role='印度人' print(egon.role,id(egon.role)) print(alex.role,id(alex.role)) print(Person.role,id(Person.role)) # #总结:在面向对象中,如果要调用一个属性,自己没有就会在类中找,如果自己有,就调用本身的
5.面向对象的组合与用法
组合用法:在一个类中以另一个类的对象作为数据属性,这个类就称为类组合
示例
class 爷爷: def __init__(self,姓名,年龄,工作): self.姓名=姓名 self.年龄=年龄 self.工作=工作 class 爸爸: def __init__(self,姓名,年龄,工作): self.姓名=姓名 self.年龄=年龄 self.工作=工作 class 儿子: def __init__(self,姓名,年龄,工作,爷爷_姓名,爷爷_年龄,爷爷_工作): self.姓名=姓名 self.年龄=年龄 self.工作=工作 self.爷爷=爷爷(爷爷_姓名,爷爷_年龄,爷爷_工作) 小强=儿子('小强',15,'学生','霸刀',87,'农民') print(小强.爷爷.姓名,小强.爷爷.年龄,小强.爷爷.工作) 小强=爸爸('大强',50,'农民') print(小强.姓名,小强.年龄,小强.工作)
圆环实例
from math import pi class Circle: def __init__(self,r): #动态参数 self.r=r #名字 对象属性 def area(self): #定义面积方法 动态属性 return pi * self.r**2 #计算面积并返回 def perimeter(self): #定义周长方法 动态属性 return pi * self.r*2 #计算周长并返回 a=Circle(10) #实例化一个圆对象 print(a.area()) print(a.perimeter()) class Ring: # def __init__(self,r1,r2,p=3.14): self.r1_circle=Circle(r1) self.r2_circle=Circle(r2) self.p=p def area(self): return self.r1_circle.area()-self.r2_circle.area() def perimeter(self): return self.r1_circle.perimeter()+self.r2_circle.perimeter() a=Ring(10,5) print(a.area()) print(a.perimeter())
6.面向对象的继承
什么叫继承
继承:什么 是 什么的关系(人和狗是动物等)
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
在python中继承分为单继承和多继承
单继承就是在子类中继承一个父类,
多继承就是在子类中继承二个或多个父类
继承的作用:减少代码的重用,提高代码的可读性,规范代码编写模式。
如下例子:
#A,B,C为定义的三个父类/基类/超类 class A: pass class B: pass class C: pass # a为子类(也叫派生类) class a(A):pass #看吧---这叫什么----这就叫单继承 # b 为子类(派生类) class b(A,B,C):pass #在看多继承。好好看
查看继承
#查看继承(__base__和__bases__) # base------从左至右查看第一个父类 print(b.__base__) # bases-----查看所有的父类 print(b.__bases__)
#注意:如果在没有指定基类/父类时,python默认继承object类,因为python中一切皆对象
print(A.__bases__)
重点来了:object是所有python类的基类
继承与抽象
抽象
1,抽象----就是将对象比较像的部分抽取成类,
2,将类比较像的部分抽取成父类
抽象最主要的部分是划分类别(各类关注点,降低复杂度)
继承:
基于抽象的结果,通过编程语言的实现,先经过抽象这个过程,在通过继承的方式表达出抽象的结构
继承与重用
#继承与重用性 # ================第一部分 #定义两个类,类之间有很多相同的部分 # 如定义猫和狗类 # 猫可以:喵喵叫,吃,喝,睡 # 狗可以:汪汪叫,吃,喝,睡 class Cat: #定义一个猫类 def mews(self): #在类里面定义方法 print('喵喵叫') def eat(self): #定义方法 print('吃') def sleep(self): #定义方法 print('睡') class Dog: def bark(self): print('汪汪叫') def eat(self): print('吃') def sleep(self): print('睡') #由上:猫和狗的多有属性相同,且猫和狗都是动物 #继承思想铺路 class Animal: #定义动物类(父类) def eat(self): print('吃') def sleep(self): print('睡') class Cat(Animal): #猫类继承动物类(子继父类)子类 def mews(self): #定义独有的方法 print('喵喵叫') # class Dog(Animal): #狗类继承动物类(子继父类)子类 def bark(self): #定义独有的方法 print('汪汪叫') #罢了,继承做它 class Animal: #父/基/超类 def eat(self): #定义吃方法 print('%s 吃'%self.name) def sleep(self): #定义睡方法 print('%s 睡'%self.name) class Cat(Animal): #子类(派生类) def __init__(self,name): #初始化 self.name = name #动态属性 self.breed = '猫' #派生属性(独有属性) def cry(self): #定义cry方法(派生方法) print('喵喵叫') class Dog(Animal): #定义狗类(派生类) def __init__(self,name): #初始化 self.name = name #动态属性 self.breed = '狗' # 派生属性(独有属性) def cry(self): #定义派生方法 print('汪汪叫') #执行 #实例化(生成对象) c1 = Cat('小黑') print(c1) print(c1.eat()) d1 = Dog('小强') print(d1.cry())
#例题
class Animal: '人和狗都是动物,所以创造一个Animal基类'
代码重用
def __init__(self,name,agg,life_value): #初始化 self.name = name #人和狗都有自己的名字 self.aggr = agg #人和狗都有自己的攻击力 self.life_value = life_value #人和狗都有自己生命值 def eat(self): #定义eat方法 print('%s is eating'%self.name) class Dog(Animal): #定义派生类 pass class Person(Animal): #定义派生类 pass #实例化对象 egg = Person('egon',10,100) #人(对象) ha2 = Dog('二狗',20,50) #狗(对象) egg.eat() #子类对象调用父类方法 ha2.eat() #子类对象调用父类方法
******子类可以继承父类同时也可以定义自己的属性---派生属性(也叫独有的属性),直接不影响父类若定义的属性与父类的重名,那么调用新增的属性时以自己为准(调用的属性在子类中有就调用在子类中的,子类中没有就调用父类中的)
注意注意:子类能调用父类的属性,父类不能调用子类的属性
那么派生来了
派生
#派生:子类继承父类就叫做派生 class Animal: #父类/基类/超类 '人和狗都是动物,所以创造有个动物类(父类/基类/超类)' def __init__(self,name,aggr,life_value): self.name = name #每个角色都有自己的名字 self.agg = aggr #每个角色都有自己的攻击力 self.life_value = life_value #每个角色都有自己的生命值 def eat(self): # 父类中定义一个共享方法 print('%s is eating'%self.name) class Person(Animal): #子类/派生类 '人类,继承动物类' def attack(self,Dog): #派生方法 '派生,人打狗的技能' Dog.life_value -= self.agg #狗的生命值等于狗的生命值减掉人的攻击(人打狗,狗掉血) class Dog(Animal): #子类/派生类 '狗类,继承也继承了Animal类' def bite(self,Person): #派生方法 '派生,狗攻击人的技能' Person.life_value -= self.agg #人的生命值等于人的生命值减掉狗的攻击(狗咬人,人掉血) #实例化 egg = Person('麦兜',500,1000) #实例化一个人对象 dog = Dog('毛发',250,1200) #实例化一个狗对象 print(egg.life_value) #查看人的生命值 print(dog.agg) #查看狗的攻击力 egg.attack(dog) #人打狗 print(dog.life_value) #查看被人打了之后狗的生命值 dog.bite(egg) #狗咬人 print(egg.life_value) #查看被狗咬过之后人的生命值
又来注意:当对象调用类属性时,先从实例化中找,在到子类中找,最后到父类中找。直至最顶层一个类
来尝例题:
# 人 狗 相同属性的同时 还有一些不同的属性 #人,狗,大战,且人有武器的游戏 # class Animal: # def __init__(self,aggressivity, life_value,name): # self.name = name # 每一个角色都有自己的昵称; # self.aggressivity = aggressivity # 每一个角色都有自己的攻击力; # self.life_value = life_value # 每一个角色都有自己的生命值; # def eat(self): # self.life_value += 10 # # class Person(Animal): # def __init__(self, name, aggressivity, life_value, money): # Animal.__init__(self, name, aggressivity, life_value) # self.money = money #派生属性:父类没有的属性 # # def attack(self,dog): # dog.life_value -= self.aggressivity # # def get_weapon(self,weapon_obj): # if self.money > weapon_obj.price: # self.money -= weapon_obj.price # 金老板花钱买武器 # self.weapon = weapon_obj # 金老板装备打狗棒 # self.aggressivity += weapon_obj.aggr # 金老板的攻击力增加了 # class Dog(Animal): # def __init__(self, name, breed, aggressivity, life_value): # Animal.__init__(self,aggressivity,life_value,name) # self.breed = breed # 每一只狗都有自己的品种; #派生属性:父类没有的属性 # # def bite(self,people): # 派生方法 :父类没有的方法 # people.life_value -= self.aggressivity # # def eat(self): # Animal.eat(self) # print('dog is eating') # snoopy = Dog('太白','京巴',250,500) # print(snoopy.breed) # print(snoopy.name) # # Animal.eat(snoopy) # snoopy.eat() # print(snoopy.life_value) # snoopy.eat() # print(snoopy.life_value)
派生属性:在自己的__init__方法里使用父类的__init__方法--------指名道姓调用方法
派生方法:在子类中增加父类没有的
父类没有子类有:找子类
子类没有父类有:找父类
子类有,父类也有:先找子类,再找父类
子类有,父类有,只想用父类的:父类名调用自己的方法(对象)
子类有,父类有,子类父类都想用,在子类中指名道姓的调用父类的方法
总结:子类有的就用子类的,只要想用父类的就父类名.父类方法(子类对象)
****在新式类中有super()方法
super()-------方法可以找父类的方法
# 在新式类 class Animal: def __init__(self,aggressivity, life_value,name): self.name = name # 每一个角色都有自己的昵称; self.aggressivity = aggressivity # 每一个角色都有自己的攻击力; self.life_value = life_value # 每一个角色都有自己的生命值; def eat(self): self.life_value += 10 class Person(Animal): def __init__(self, name, aggressivity, life_value, money): # Animal.__init__(self, name, aggressivity, life_value) super().__init__(name, aggressivity, life_value) #新式类 self.money = money #派生属性:父类没有的属性 def attack(self,dog): dog.life_value -= self.aggressivity def get_weapon(self,weapon_obj): if self.money > weapon_obj.price: self.money -= weapon_obj.price # 金老板花钱买武器 self.weapon = weapon_obj # 金老板装备打狗棒 self.aggressivity += weapon_obj.aggr # 金老板的攻击力增加了 class Dog(Animal): def __init__(self, name, breed, aggressivity, life_value): # Animal.__init__(self,aggressivity,life_value,name) # super(Dog,self).__init__(aggressivity,life_value,name) super().__init__(aggressivity,life_value,name) self.breed = breed # 每一只狗都有自己的品种; #派生属性:父类没有的属性 def bite(self,people): # 派生方法 :父类没有的方法 people.life_value -= self.aggressivity def eat(self): # Animal.eat(self) super().eat() print('dog is eating') snoopy = Dog('太白','京巴',250,500) print(snoopy.breed) print(snoopy.name) snoopy.eat() print(snoopy.life_value) super(Dog,snoopy).eat() #Animal.eat(snoopy) print(snoopy.life_value) # 用子类的对象,调用父类的方法: # 如果子类中没有这个方法,直接就使用父类的 # 如果子类中有同名方法: # 经典类 指名道姓 类名.方法名(子类对象) 类内外一致 # 新式类 super方法 super(子类名,子类对象).方法名() 类内可以省略super的参数
总结一下下:在新式类中,super()方法其实是替换了父类名调用__init__方法,但是不同于父类名调用__init__方法的是:如果定义了多个父类,super使用父类的方法时找最近的那个父类,如果在多个父类的情况下要使用任意一个类的方法,就要指名道姓的调用使用。。。。。。
如:super().__init__(name,aggr,life_value)
记得super加括号调用__init__方法(self不用写了,只传入属性)
钻石继承
钻石继承是经典类和新式类的多继承问题,继承顺序问题
来看经典类先,(注意,经典类只有python2版本中有,python3中只有新式类)
******D是B和C的父类,B和C是A的父类。
重要非常-----python2与python3中的异同:
python2中有经典类和新式类,python3中只有新式类
#经典类 class B: pass class C(B): pass #新式类 class B(object): pass class C(B): pass
上图经典类(深度优先)和新式类(广度优先)查找顺序
深度优先查找顺序:A----->B------>D-------->F------>C-------->E
广度优先查找顺序:A----->B------>D------->C------->E-------->F
深度优先为经典类,广度优先为新式类
经典类例题,以深度优先查找
class F(): def f(self): print('F') class E(F): def f(self): print('E') class D(F):pass # def f(self): # print('D') class B(D):pass # def f(self): # print('B') class C(E): def f(self): print('C') class A(B,C):pass # def f(self): # print('A') a = A() a.f()
新式类示例,以广度优先查找,
class F(object): ---------在python2版本中经典类与新式类的区别就在这 def f(self): print('F') class E(F): def f(self): print('E') class D(F):pass # def f(self): # print('D') class B(D):pass # def f(self): # print('B') class C(E): def f(self): print('C') class A(B,C):pass # def f(self): # print('A') a = A() a.f()
******而在python3版本中默认都为新式类,没有经典类。也就是广度优先
------查看继承顺序 (第一个类名调用mro方法)
有两种方式(经典类中没有此方法):
#如上例题查找继承顺序 A.mro() == A.__mro__ print(A.mro()) print(A.__mro__) # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>] # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>)
来吧,看面试题
class Foo: def __init__(self): self.func() def func(self): print('Foo.func') class Son(Foo): def func(self): print('Son.func') s = Son()
对上题做做思路:首先在程序最后面生成的是Son对象,所以先执行Son类,但是Son为Foo的子类而且这里Son(Foo)是派生类,那么执行Foo代码,在func方法中打印'Foo.func',别急,再看派生类中,人家也有func派生方法func,恰好也有输出,所以结果是‘Son.func’
*****在继承中,子类有的方法就优先执行子类的,子类中没有的就执行父类的。
多态
首先说明:python中处处是多态。然而有些人却说python中没有多态,简直错误。
python本身自带多态
什么是多态:一类事物有多种形态
class Animal: pass class person(Animal): def attack(self): pass class dog(Animal): def attack(self): pass def attack(obj): #这个就是多态 obj.attack() d = dog() p = person() attack(d) # d.attack() attack(p) #p.attack()
比如:人,狗,猫是动物的多种形态
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractclassmethod def talk(self): pass class Person(Animal): #形态一 人 def talk(self): print('say hello') class Dog(Animal): #形态二 狗 def talk(self): print('say wangwang') class Cat(Animal): #形态三 猫 def talk(self): print('say miaomiao')
多态性
什么是多态性-----在不考虑实例类型的情况下使用实例
# 多态性 #peo,dog,pig都是动物,所以都有talk方法 # peo = People() # dog = Dog() # pig = Pig() # peo.talk() # dog.talk() # pig.talk()
鸭子类型(有相同类型,相同方法等特性)
看上起来像,走起路来像,样子像鸭子就是鸭子-----根据特征相同定义事物
如:列表,元组就是一对鸭子类型,都是序列,都有len方法
python崇尚鸭子类型
class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass
封装
1、狭义上的封装:把属性和方法藏在类中(只能在类内部调用,不能在类外部调用).隐藏对象的属性和实现的细节值
class Dog: __res = 'dog' #私有属静态属性 当变量名前加双下划线时,就是把属性藏起来 **注:属性可以私有,方法也可以私有 def func(self): print(Dog.__res) #python默认:_类名__属性名 #在类的外面不能直接调用, print(Dog._Dog.__res) #语法不支持,不能这样调用,在类内加上一层密码 _类名 d = Dog() d.func()
2、广义上的封装:把一些属性和方法放到类中本身就是一种封装 (只能通过类名调用方法和属性。不能直接调用),
class Dog: ret = 'dog' print(Dog.ret)
分装的好处:将变化隔离,提高复用性,便于使用,提高安全性
私有变量和私有方法
在python中用双划线开头的方式将属性隐藏起来(设置成私有的)
私有变量
#其实这仅仅这是一种变形操作 #类中所有双下划线开头的名称如_x都会自动形成:_类名__x的形式: class A: _N = 0 #类中数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如_N,会变形为_A__N def __init__(self): self.__X = 10 #变形为self._A__X def __foo(self): #变形为_A__foo访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形 print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到 #A._A__N是可以
这种自动变形的特点:
1,类中定以的__x只能在内部使用,如self.__x,引用的就是变形的结果
2,这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的
3,在子类定义的__X不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即上下划线开头的属性在继承给子类时,子类时无法覆盖的
双下方法:
class Dog: __res = 'dog' #私有属静态属性 当变量名前加双下划线时,就是把属性藏起来 def __discount(self): #私有方法 print('in_func') def price(self): self.__discount() #在内部悄悄地调用 # 不管是私有的属性,私有的方法都是私有的
注::
# 在类中可以定义私有属性,私有方法,定义在类里面的私有属性和私有方法只能在类中调用,
# 在类外也可以调用但不常用,麻烦,不推荐这样做
*/*定义一个私有变量,在类内可以直接调用,在类外不能直接调用,如果一定要用,必须在私有方法之前加上:_类名。
如:
print(Dog._Dog.__res)
在类外的名字可以通过__dict__方法就可以查看。
实际例子:
为什么要将属性封装起来:不像被外部调用
1,涉及安全 2,没必要
class Room: def __init__(self,name,price,lenght,width): self.name = name self.price = price self.__lenght = lenght #私有的对象属性 self.__width = width #私有属性 def area(self): return self.__lenght*self.__width house = Room('胜哥',100000,120,100) print(house.area())
私有的:私有的有静态属性,方法,对象属性,使用__名字的方法调用,保证在类内部可以调用,外部不行
当一个名字不想被外部使用也不想被自类继承,只想在内部调用的时候就定义私有的
#私有方法 #把fa定义成私有的,即__fa class A: def __fa(self): #在定义时就变形为_A__fa print('from A') def test(self): self.__fa() class B(A): def __fa(self): #只能与自己所在的类为准,即调用_A__fa print('from B') b = B() b.test()
property属性
什么是特性property
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
class People: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height @property #伪装属性 def bmi(self): return self.weight / (self.height**2) p1 = People('egon',75,1.85) print(p1.bmi)
import math class Circle: def __init__(self,radius): #圆的半径radius self.radius = radius @property def area(self): return math.pi*self.radius**2 #计算面积 @property def perimeter(self): return 2*math.pi*self.radius #计算周长 c = Circle(10) print(c.radius) print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) #同上
为什么要用property
将一个类的函数定义成特性后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行一个函数然后计算出来,这种特性的使用方式遵循了统一访问的原则
ps:面向对象的封装有三种方式:
【public】----这种其实就是不分装,对外开放
【peotected】-----这种封装对外不公开,但对朋友或子类(儿子)公开
【peivate】-----这种封装对谁都不公开
python中通过property方法可以实现
class Foo: def __init__(self,val): self.__name = val #将所有的数据属性都隐藏起来 @property def name(self): return self.__name #obj.name访问的是self.__name(这也是真实值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在设定值之前进行类型检查 raise TypeError('%s must be str' %value) self.__name = value #通过类型检查后,将值value存放到真实的位置self.__name @name.deleter def name(self): raise TypeError('Can not delete') f = Foo('egon') print(f.name) f.name = 10 #抛出异常'TypeError: 10 must be str' del f.name #抛出异常'TypeError: 10 Can not delete'
一个静态属性property本质就是实现了get,set,delete三种方法
class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1 = Foo() f1.AAA f1.AAA = 'aaa' def f1.AAA
class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self,value): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA
class Goods: def __init__(self): #原价 self.original_price = 100 #折扣 self.discount = 0.8 @property def price(self): #实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self,value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price #获取价格 obj.price = 200 #修改商品原价 print(obj.price) del obj.price #删除商品原价
classmethod
class Classmethod_Demo(): role = 'dog' @classmethod def func(cls): print(cls.role) Classmethod_Demo.func()
staticmethod
class Statimethod_Demo(): role = 'dog' @staticmethod def func(): print('当普通方法用') Statimethod_Demo.func()
面向对象的进阶:
面向对象里面用到了函数的内置方法有:property,classmethod,staticmethod,supper,object,1,isinstance,issubclass 两者用于判断,返回结果不是布尔值
isinstance(对象,类)判断这个对象是不是这个类或者这个类的子类的实例化
class Foo(object): pass obj = Foo() #实例化对象 print(isinstance(obj,Foo)) #实例化的对象obj是不是Foo这个类的实例化 class A:pass #定义A类/父类 class B(A): #定义子类/派生类 pass b = B() 实例化b对象 # print(isinstance(b,A)) #判断b这个对象是不是A这个类的实例化 True # print(isinstance(a,object)) #判断a对象是不是object这类的实例化 报错,没有a对象
from collections import Iterable
print(isinstance([],Iterable)) #判断列表是不是可迭代对象
issubclass(派生类,父类)判断一个类是不是另一个类的派生类
class Foo: #父类/基类/超类 pass class Boo(Foo): #子继父类/派生类 pass print(issubclass(Boo,Foo)) #判断Boo类是不是Foo的派生类 True class A:pass class B(A):pass print(issubclass(B,A)) #判断B是不是A的派生类 True print(issubclass(A,B)) # A为父类,不是B的派生类 False print(issubclass(A,object)) #A是object的派生类 object是所有类的父类,所有类且继承object,所以所有类是object的派生类 True print(issubclass(B,object)) #B是obiect的派生类 True
2,反射
什么是反射--------程序可以访问,检测,修改它本身状态和行为的一种能力(自省)
python面向对象中的反射:指的是通过字符串的形式操作对象的相关属性(python中的一切事物都是对象),所以都可以使用反射
反射的四个内置函数:
getattr-----------------------获取成员
hasattr----------------------检查成员
setattr-----------------------设置成员
delattr-----------------------删除成员
以上内置函数适用于类和对象(一切皆对象,类本身也是一个对象)
def getattr(object,name,default = None): #已知的getattr特殊属性 ''' getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. ''' pass def hasattr(*args,**kwargs): #real dignature unknown ''' Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. ''' pass def setattr(x,y,v): #real signature unknown;renstored from_doc_ ''' Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' ''' pass def delattr(x,y): # real sugnature unknown; restored from _doct_ ''' Deletes the named arrtibute form given object. delattr(x,'y') is equivkent to 'del x.y' ''' pass
class Foo(object): #定义Foo类 def __init__(self): self.name = 'abc' def func(self): #定义func方法 return 'ok' #返回ok obj = Foo() #实例化对象 # 获取成员 ret = getattr(obj,'func') #获取的是个对象 r = ret() print(r) # 检查/判断成员 ret = hasattr(obj,'func') #因为有func方法所以返回True print(ret) #查看结果 #设置成员 print(obj.name) #设置之前为'abc' ret = setattr(obj,'name',19) print(obj.name) #设置之后为19 # 删除成员 print(obj.name) #删除之前为19 ret = delattr(obj,'name') #执行删除 print(obj.name) #报错,证明删除成功
class Foo: f = '类的静态变量' def __init__(self,name,age): self.name = name self.age = age def say_hi(self): print('hi,%s'%self.name) obj = Foo('egon',37) #实例化对象 #获取属性 n = getattr(obj,'name') #获取属性 print(n) #获取对象 func = getattr(obj,'say_hi') #获取属性 func() #调用 print(getattr(obj,'aaaaaa','不存在啊')) # # 检测是否含有某属性 返回布尔值 n = hasattr(obj,'name') #判断 print(n) n = hasattr(obj,'say_hi') print(n) # 设置属性 setattr(obj,'sb',True) setattr(obj,'show_name',lambda self:self.name+'sb') print(obj.__dict__) print(obj.show_name(obj)) # 删除属性 delattr(obj,'age') delattr(obj,'show_name') delattr(obj,'show_namelll') #不存在,则报错 print(obj.__dict__)
类也是对象
# 类也是对象 class Foo(object): staticField = 'old boy' #静态属性 def __init__(self): self.name = 'egon' #定义egon属性 def func(self): #定义func方法 return 'func' #返回func @staticmethod #语法糖 def bar(): return 'bar' print(getattr(Foo,'staticField')) print(getattr(Foo,'func')) print(getattr(Foo,'bar'))
反射当前模块
#反射当前模块成员 import sys def s1(): print('s1') def s2(): print('s2') this_module = sys.modules[__name__] hasattr(this_module,'s1') getattr(this_module,'s2')
类的(双下)内置方法
__str__ 和 __repr__
__repr__是__str__的备胎
#__str__和__repr__ #改变对象的字符串显示__str__和__repr__ #自定制格式化字符串__format__ format_dict = { 'nat':'{obj.name} - {obj.addr} - {obj.type}', #学校名-学校地址-学校类型 'tna':'{obj.type} - {obj.name} - {obj.addr}', #学校类型-学校名:学校地址 'tan':'{obj.type} - {obj.addr} - {obj.name}', #学校类型/学校地址/学校名 } class School: def __init__(self,name,addr,type): self.name = name self.addr = addr self.type = type def __repr__(self): return 'School(%s,%s)'%(self.name,self.addr) def __str__(self): return '(%s,%s)'%(self.name,self.addr) def __format__(self, format_spec): if not format_spec or format_spec not in format_dict: format_spec = 'nat' fmt = format_dict[format_spec] return fmt.format(obj = self) s1 = School('oldboy1','北京','私立') print('from repr:',repr(s1)) print('from srt:',str(s1)) print(s1) ''' srt函数或者print函数---》obj.__srt__() repr或者交互式解释器----》obj.__repr__() 如果__srt__没有被定义,那么就会使用__repr__来代替输出 注意:这两种方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd'))
class A: pass def __str__(self): return 'A的对象' def __repr__(self): return 'repr:A的对象' a = A() print(a) #本质调用的是__str__,如果没实现,就调用__repr__,在找不到就用object父类的 print(a.__str__()) #str却不能做repr的备胎 print(a.__repr__()) #repr是str的备胎 #打印一个对象的时候,实际上是调用了这个对象所在类的__str__方法,打印的是这个方法的返回值 print(str(a)) print(repr(a)) print('%s'%a) print('%r'%a)
%s和%r
# %s和%r class B: def __str__(self): return 'str:class B' def __repr__(self): return 'repr:class B' b = B() print('%s'%b) print('%r'%b)
__del__ 析构方法
删除的时候对相关的也要进行处理,就用析构方法
# __del__析构方法 # 析构方法,当对象在内存中被释放时,自动触发执行。无须定义
#先执行代码中的内容,再删除对象
#如果删掉这个对象,这个对象的其他附属附属内容也没有用了,就认为被回收掉了 # 简单例子 class Foo: def __del__(self): print('执行我啦') f1 = Foo() print(f1) del f1 print('----->')
item系列
关于item,对象访问如果是对象名[ ] 形式,是因为对象内部实现了item系列方法
# __getitem__\__setitem__\__delitem__ class Foo: def __init__(self,name): self.name = name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): 等待del触发执行 print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1 = Foo('sb') f1['age'] = 18 给f1添加一个属性 f1['age1'] = 19 del f1.age1 删除属性 del f1['age'] f1['name'] = 'alex' print(f1.__dict__)
__new__方法是创造一个裸地对象,new方法和init方法的顺序是,先执行new方法,在执行init方法
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) a = A() print(a.x)
单例模式
class Singleton: def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): cls._instance = object.__new__(cls, *args, **kw) #创建一个新对象 return cls._instance one = Singleton() # obj = object.__new__() two = Singleton() two.a = 3 print(one.a) # 3 # one和two完全相同,可以用id(), ==, is检测 print(id(one)) # 29097904 print(id(two)) # 29097904 print(one == two) # True print(one is two)
__call__ 对象后面加括号,触发执行
注:构造方法的执行时由创建对象触发的,对象 = 类名();而对于__call__方法的执行是由对象后加括号的,即:对象()或者类()() class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() #执行__init__ print(obj) obj() #执行__call__
约定:对象名() 类后面加括号就是调用这个库,这种方法只用于__call__
obj()()
对象后面的括号和__call__是约定相关关系
__len__
class A: def __init__(self): self.a = 1 定义属性a = 1 self.b = 1 self.c = 1 这里定义几个属性长度就是几 def __len__(self): —__len__对应len方法 return len(self.__dict__) a = A() print(len(a))
在内置函数len什么实质是调用__len__方法
__hash__
# __hash__ class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a)+str(self.b)) a = A() print(hash(a)) 这里的hash和return---hash都等同于__hash__ hash调用内置的__hash __
__eq__
class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self, obj): 这里的__eq__就是equar-----相等的意思 if self.a == obj.a and self.b == obj.b: return True #函数返回True表示等式成立。return False print 永远不相等 a = A() b = A() 主函数都没有,print则不相等 print(a == b) 这里相等是是因为return了True 判断两个数相等的时候是调用内置的__eq__方法
可以没有判断条件,只要返回True,等式成立
纸牌游戏来啦
#纸牌游戏 from collections import namedtuple Card = namedtuple('Card',['rank','suit']) class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') suits = ['红心','方板','梅花','黑桃'] print(ranks) print(suits) def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] for rank in FranchDeck.ranks: #每循环一次就拿到一张扑克牌, for suit in FranchDeck.suits: self._cards.append(Card(rank,suit)) #再将这张扑克牌添加到了列表里面去 # 实现了抽牌功能 def __len__(self): return len(self._cards) def __getitem__(self, item): return self._cards[item] # 实现洗牌功能 def __setitem__(self, key, value): self._cards[key] = value deck = FranchDeck() #实例化 ,实现init里面的属性 print(deck._cards) print(deck[0]) from random import choice print(choice(deck)) print(choice(deck)) #实现洗牌 from random import shuffle shuffle(deck) print(deck._cards)
面试题
内置方法解决
# 一个类有100个对象,每个对象有三个属性:name,age,sex、 # 如果两个对象的name和sex完全一致 # 请对这100个对象进行去重 class Person: #创建一个类 def __init__(self,name,age,sex): #定义三个属性 self.name = name self.age = age self.sex = sex #两个的hash结果相等且a == b,才去重 def __hash__(self): #定义hash方法 return hash(self.name+self.sex) #两个字符串拼接起来取hash值 def __eq__(self, other): #定义equar方法,判断相同 #判断自己的姓名,性别和self的姓名性别相同,返回True if self.name==other.name and self.sex == other.sex:return True p_lst = [] #建立一个空列表 for i in range(100): #实例化100各个对象 p_lst.append(Person('egon',i,'male')) #将这100个对象添加到列表中 print(p_lst) print(set(p_lst)) #给100个对象去重