python面对对象-初学
中间隔有python模块没有进行更新,是因为觉得模块的操作应用更应该记牢,再次先不更新
1,面对对象的作用: 能简化思路和代码
2,面对对象的思路:
不着急直接去计算/操作代码获取结果
从一个大的问题中,找到要描述的角色
将角色的属性和行为抽象出来
形成一个模子
后期用到角色的时候,都围绕这个模子来操作。
人狗大战
#人狗大战 #角色 #alex = {'name':'alex','sex':None,'hp':10,'ad':1} #taibai = {'name':'哮天犬','kind':'teddy','hp':100,'ad':50} def Person(name,sex,hp,ad): #创造一个模子,约束一个人必须有的属性,规范属性的名字,简化了用户的操作 person = { 'name':name, 'sex':sex, 'hp':hp, 'ad':ad } def attack(dog): #攻击 print('%s攻击了%s'%(person['name'],dog['name'])) dog['hp'] -= person['ad'] print('%s掉了%s点血,剩余血量%s'%(dog['name'],person['ad'],dog['hp'])) person['attack']=attack return person #狗的特性及能力 def Dog(name,sex,hp,ad): dog = { 'name':name, 'kind':sex, 'hp':hp, 'ad':ad } def bite(person): print('%s咬了%s'%(dog['name'],person['name'])) if person['hp']<=dog['ad']: person['hp'] = 0 #疑问 这里更改变量 全局里的会发生改变吗 print('剩余血量以为零,GAMEOVER') else: person['hp'] -= dog['ad'] print('%s掉了%s点血,剩余血量%s'%(person['name'],dog['ad'],person['hp'])) dog['bite'] = bite return dog alex = Person('alex',None,10,1) taibai = Dog('哮天犬','teddy',100,50) alex['attack'](taibai) taibai['bite'](alex)
#类: 一类抽象的事物,是描述了一类事物有哪些属性或者行为,但不并不具体
#实例: 就是一个依托于类的规范存在的,被赋予了具体属性值得实际存在的物体
#对象:对象就是实例 这两个是一个 只是不同的说法而已
#实例化: 有一个类产生一个对象/实例的过程
************************************************************************************************************************************************
函数式编程vs面向对象编程
# 函数式编程 # auth 认证相关 def login(): pass def regisgter(): pass # account 账户相关 def func1(): pass def func2(): pass # 购物车相关 def shopping(username,money): pass def check_paidgoods(username,money): pass def check_unpaidgoods(username,money): pass def save(username,money): pass 函数式编程
#面对对象编程
class LoginHandler: def login(self): pass def regisgter(self): pass class Account: def func1(self): pass def func2(self): pass class ShoppingCar: def shopping(username,money): pass def check_paidgoods(username,money): pass def check_unpaidgoods(username,money): pass def save(username,money): pass 面向对象式编程
面向对象编程:是一类相似功能函数的集合,使你的代码更清晰化,更合理化。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。
那什么是类?什么是对象?
类:就是具有相同属性和功能的一类事物。
对象:就是类的具体表现。
具体一些:先解释解释什么是⻋? 有轱辘, 有方向盘, 有发动机, 会跑的是车。 好,在解释一个. 什么是人:有名字, 年龄, 爱好, 会唱歌跳舞思考的是人。那么广义上 车,人就是类:但是具体的我的车,你这个人这是一个对象。
猫,是一类,你们家养的 大橘。
狗,是一类,隔壁家养的那只二哈就是对象。
3,类 :一类抽象的事物,是描述了一类事物有哪些属性或者行为,但是不具体。
类的结构:
class Human: """ 此类主要是构建人类 """ mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段 就是一个变量 dic = {} l1 = [] def work(self): # 第二部分:方法 函数 动态属性 ---自带一个叫做self的形参,约定俗成的 print('人类会工作')
print(Human.mind) #查看类里面的静态属性
print(Human.work(1)) #执行类里面的函数 这里要注意self 也是要传的参数
print(Human.__dict__)#将类里所有的属性存在这个字典里 class 是关键字与def用法相同,定义一个类。 Human是此类的类名,类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头。 类的结构从大方向来说就分为两部分: 静态变量。 动态方法。
类名可以做两件事:
查看静态属性和动态属性
创造一个对象-->类名加() 就是创造了一个对象
会自动的触发有__init__的方法
#类里面的传参是传到有 __init__的方法里 class Person: Country = 'China' def __init__(self,name,sex,hp,ad): print('self',self) obj = Person('alex',None,100,50) print('obj',obj) 这个obj的地址和self的地址一致,同一个地址
类的相关知识
类名操作静态属性
第一种,查看类中的所有内容:类名.__dict__方式。
class Human: """ 此类主要是构建人类 """ mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段 dic = {} l1 = [] def work(self): # 第二部分:方法 函数 动态属性 # print(self) print('人类会工作') print(Human.__dict__) print(Human.__dict__['mind']) Human.__dict__['mind'] = '无脑' print(Human.__dict__) # 错误 #通过这种方式只能查询,不能增删改. # 第一种方式只用户查询全部内容(一般不用单独属性查询).
第二种:万能的点.
class Human: """ 此类主要是构建人类 """ mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段 dic = {} l1 = [] def work(self): # 第二部分:方法 函数 动态属性 # print(self) print('人类会工作') print(Human.mind) # 查 Human.mind = '无脑' # 改 print(Human.mind) del Human.mind # 删 Human.walk = '直立行走' print(Human.walk) # 通过万能的点 可以增删改查类中的单个属性
对以上两种做一个总结:如果想查询类中的所有内容,通过 第一种__dict__方法,如果只是操作单个属性则用万能的点的方式。
类名操作动态方法
class Human: """ 此类主要是构建人类 """ mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段 dic = {} l1 = [] def work(self): # 第二部分:方法 函数 动态属性 # print(self) print('人类会工作') def tools(self): print('人类会使用工具') Human.work(111) Human.tools(111) 下面可以做,但不用。 Human.__dict__['work'](111)
类有两种作用:属性引用和实例化
属性引用(类名.属性)
class Person: #定义一个人类 role = 'person' #人的角色属性都是人 def walk(self): #人都可以走路,也就是有一个走路方法 print("person is walking...") print(Person.role) #查看人的role属性 print(Person.walk) #引用人的走路方法,注意,这里不是在调用
实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
class Person: #定义一个人类 role = 'person' #人的角色属性都是人 def __init__(self,name): self.name = name # 每一个角色都有自己的昵称; def walk(self): #人都可以走路,也就是有一个走路方法 print("person is walking...") print(Person.role) #查看人的role属性 print(Person.walk) #引用人的走路方法,注意,这里不是在调用
实例化的过程就是类——>对象的过程
原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。
语法:对象名 = 类名(参数)
egg = Person('egon') #类名()就等于在执行Person.__init__() #执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。
查看属性&调用方法
print(egg.name) #查看属性直接 对象名.属性名 print(egg.walk()) #调用方法,对象名.方法名()
类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲):只会查看从左到右第一个类 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中) 类属性的补充
对象的相关知识
什么是对象
对象是从类中出来的,只要是类名加上(),这就是一个实例化过程,这个就会实例化一个对象。
class Human: mind = '有思想' def __init__(self): print(666) print(self) # <__main__.Human object at 0x00000191508AA828> def work(self): print('人类会工作') def tools(self): print('人类会使用工具') obj = Human() # 只要实例化对象,它会自动执行__init__方法 print(obj) # <__main__.Human object at 0x00000191508AA828> # 并且obj的地址与self的地址相同
其实实例化一个对象总共发生了三件事:
1,在内存中开辟了一个对象空间。
2,自动执行类中的__init__方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
3,在__init__ 方法中通过self给对象空间添加属性。
class Human: mind = '有思想' language = '使用语言' def __init__(self,name,sex,age,hobby): # self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间封装四个属性。 self.n = name self.s = sex self.a = age self.h = hobby obj = Human('barry','男',18,'运动')
通过函数来理解对象
def Person(*args,**kwargs): self = {} def attack(self,dog): dog['life_value'] -= self['aggressivity'] def __init__(name,aggressivity,life_value): self['name'] = name self['aggressivity'] = aggressivity self['life_value'] = life_value self['attack'] = attack __init__(*args,**kwargs) return self egg = Person('egon',78,10) print(egg['name'])
对象操作对象空间属性
1)对象查询对象中所有属性。 对象.__dict__
class Human: mind = '有思想' language = '实用语言' def __init__(self,name,sex,age,hobby): # self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间封装四个属性。 self.n = name self.s = sex self.a = age self.h = hobby obj = Human('barry','男',18,'运动') print(obj.__dict__) # {'n': 'barry', 'h': '运动', 's': '男', 'a': 18}
2)对象操作对象中的单个属性。 万能的点 .
class Human: mind = '有思想' language = '实用语言' def __init__(self,name,sex,age,hobby): # self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间封装四个属性。 self.n = name self.s = sex self.a = age self.h = hobby obj = Human('barry','男',18,'运动') obj.job = 'IT' # 增 del obj.n # 删 obj.s = '女' # 改 print(obj.s) # 查 print(obj.__dict__)
对象查看类中的属性
class Human: mind = '有思想' language = '实用语言' def __init__(self,name,sex,age,hobby): self.n = name self.s = sex self.a = age self.h = hobby obj = Human('barry','男',18,'运动') print(obj.mind) print(obj.language) obj.a = 666 print(obj.a)
对象操作类中的方法
class Human: mind = '有思想' language = '实用语言' def __init__(self,name,sex,age,hobby): self.n = name self.s = sex self.a = age self.h = hobby def work(self): print(self) print('人类会工作') def tools(self): print('人类会使用工具') obj = Human('barry','男',18,'运动') obj.work() obj.tools()
类中的方法一般都是通过对象执行的(除去类方法,静态方法外),并且对象执行这些方法都会自动将对象空间传给方法中的第一个参数self.
self 是什么?
self其实就是类中方法(函数)的第一个位置参数,只不过解释器会自动将调用这个函数的对象传给self。
所以咱们把类中的方法的第一个参数约定俗成设置成self, 代表这个就是对象。
********************************************************************************************************************************
实例化: 由一个类产生一个对象/实例的过程。 创建对象的过程 对象=类名()
#1,创建一个对象 产生一些类对象指针
#2,调用init方法,将创造的对象传给self,执行init方法
#3,将self自动的返回给调用者及对象
创造一个函数:用def(开头用小写)
创造一个类:用class(一般开头要用大写)
class Person: 这个整体就是类 country = ‘China’#静态属性 def func(self): print(123) def aaa(self): Person 是一个类名
Person()#这就是对象
Person 中可以放两个东西(类中可以定义的):
静态属性:就是变量--是所有的对象共享的一个属性
动态属性(方法):就是函数 自带一个叫做self的形参 特殊的__init__方法
类中写的所有的静态属性和动态属性都是所有对象共享的。
在self空间中或者是在实例化之后的对象的空间中定义的变量都是属于某个对象的
类名可以做两件事情:
查看静态属性和动态属性
创造一个对象 也就是实例化
对象:调用方法 查看对象命名空间中的属性
class Person: Country = 'China' def func(self): print(123) def __init__(self,name,sex,hp,ad): print() print(Person.Country) #通过.连接其中的元素 print(Person.func) Person.func(0) #使用Person调用方法的时候 self也是一个需要传的参数 print(Person.__dict__) #内置的一个字典 将变量和函数存进去 #创造一个对象的过程:对象 = 类名+() #首先要创造一个对象 #会自动的触发__init__函数,将创造出来的对象的地址创给self(self和obj是同一个东西) #执行init中的代码 #将self指向的地址再返回给调用者 obj = Person('alex',None,10,1) #会自动化执行 def __init__(self):
需要注意:
#这时候传的参数还没有传到self所指向的内存空间里,因为在内存空间里,self 与name,sex等参数还未有联系 class Person: Country = 'China' def __init__(self,name,sex,hp,ad): pass obj = Person('alex',None,100,50) class Person: Country = 'China' def __init__(self,name,sex,hp,ad):#将参数与self关联-初始化方法 self.user = name #这里self.后面的名称可以起任意名 self.sex=sex self.hp = hp self.ad = ad print(self.__dict__)#{'name':'alex','sex':None,'hp':100,'ad':50} #和人狗大战里面的dic很像,只是换了一种方法 obj = Person('alex',None,100,50)#实例化 print(obj.user)
学过对象后,优化人狗大战代码:下面加了self.name=name...这样self和形参才关联起来
class Person: def __init__(self,name,sex,hp,ad): self.name=name self.sex=sex self.hp=hp self.ad=ad def attack(self,dog): #攻击 print('%s攻击了%s'%(self.name,dog.name)) dog.hp -= self.ad print('%s掉了%s点血,剩余血量%s'%(dog.name,self.ad,dog.hp)) class Dog: def __init__(self,name,kind,hp,ad): self.name=name self.kind=kind self.hp=hp self.ad=ad def bite(self,person): print('%s咬了%s'%(self.name,person.name)) person.hp -= self.ad if person.hp>0: print('%s掉了%s点血,剩余血量%s'%(person.name,self.ad,person.hp)) else: person.hp = 0 print('gameover') alex = Person('alex',None,10,1) #实例化 #print(alex.__dict__) taibai = Dog('哮天犬','teddy',100,50)#实例化 alex.attack(taibai) #相当于---->Person.attack(alex,taibai) 记住Alex就是self
对象的执行过程:为什么用类对象指针 节能内存,如果人特别多,不可能将方法存在每个用户内存里,先有类 后有对象
对象名可以调用静态属性--->类名空间与对象空间
class Person: Country = 'China' def __init__(self,name): #这个叫初始化方法 self.name=name def attack(self): print('attack') alex = Person('alex') egon = Person('egon') alex.Country = '印度' #往Alex的对象内存中存了一个印度 print(alex.Country) #印度 print(egon.Country) #China print(Person.Country) #China class Person: Country = ['China'] def __init__(self,name): self.name=name alex = Person('alex') egon = Person('egon') alex.Country[0] = '印度' print(alex.Country) #印度 print(egon.Country) #印度 print(Person.Country) #印度
在执行对象前 会先创造一个对象的名称空间,对象空间里面存有类对象指针 可以指向类里的名称空间,
对象所使用的静态先在对象自己的空间里找,如果找不到再到类空间里找,如果类里也没有,则会在对象自己空间里创造一个静态属性
总结:如果是对静态属性赋值,那么会在对象空间里创造一个 如果是像列表一样更改 那么会先去找类空间找,找不到则再创造。
首先静态变量之所以出现:所有的对象都共享的值
调整静态变量的值:直接用类名去调整,是最准确的
对象对一个静态变量重新赋值的过程,实际上是在对象所在的内存空间中 添加一个个同名属性而已
方法并没有真正存储在对象的空间中,之所有能找到,是因为每一个对象在创建之初都有一个执行类的指针。
类名:
查看修改静态属性
实例化
对象名:
查看对象命名空间里的属性
调用方法
对象使用名字的特点:自已有的时候用自己的,自己没有的时候用类的
静态属性和对象属性的区别是什么?下面的圆有讲解
如果所有的对象的某一个值都是相同的,就用静态属性
如果所有的对象的某一个值都是不同,就用对象属性
************************************组合**************************************
组合:两个类的事儿,描述的是类之间的关系
关系:一个类有另一个类 比如人有武器
一个类的对象作为另一个类对象的属性
武器类的对象作为人类对象的一个属性
class Person: def __init__(self,name,sex,hp,ad): self.name = name self.sex=sex self.hp=hp self.ad=ad def attack(self,dog): dog.hp -= self.ad class Dog: def __init__(self,name,kind,hp,ad): self.name = name self.kind=kind self.hp=hp self.ad=ad def bite(self,person): person.hp -= self.ad class Weapon: def __init__(self,name,price,ad=0,hp=0): self.name = name self.price = price self.ad = ad self.hp = hp self.ad_total = self.ad #人和武器的攻击力总和 def Kill(self,dog): dog.hp -= self.ad_total alex = Person('alex',None,50,1) taibai = Dog('哮天犬','teddy',200,30) alex.attack(taibai) print(taibai.hp) 板儿砖 =Weapon('打狗砖',998,190) print(板儿砖.__dict__) alex.weapon = 板儿砖 #在这里是将这个板儿砖的属性添加到Alex的对象空间中 alex.weapon.Kill(taibai) #alex.weapon就是用板儿砖---->组合 print(taibai.hp)
学生与课程组合
课程类的对象作为学生对象的一个属性
class Student: def __init__(self,name,sex,age,number,course): self.name = name self.sex = sex self.age = age self.number = number self.course = course class Course: def __init__(self,name,period,price): self.name = name self.period = period self.price = price python = Course('python','6 months',20000) xiaoming = Student('小明','male',18,10086,python) xiaoming.course.name #这就是一个组合 print(xiaoming.course.name) print(xiaoming.course.period) print(xiaoming.course.__dict__)#查看小明所上课程的全部属性
圆和圆环组合:圆形类的对象作为圆环对象的一个属性
实例化: 创建对象的过程 对象=类名()
1,创建一个对象,产生一些类对象指针
2,调用init方法,将创造的对象传给self,执行init方法
3,将self自动的返回给调用者
class Circle: def __init__(self,r): self.r = r def perimeter(self): return 3.14*2*self.r def area(self): return 3.14*self.r**2
from math import pi #在这里pi是共用属性 都是相同的 就用静态属性 print(pi) class Circle: #目的是规范一个圆 def __init__(self,r): self.r = r def perimeter(self): return pi*2*self.r def area(self): return pi*self.r**2 class Ring: def __init__(self,out_r,in_r): self.out_circle = Circle(out_r) self.in_circle = Circle(in_r) def peri(self): return self.out_circle.perimeter() + self.in_circle.perimeter() def are(self): return self.out_circle.area() - self.in_circle.area() r = Ring(10,5) print(r.peri()) print(r.are())
圆的创建
*************面对对象的三大特性***************
python重要的是继承和封装
继承
多态
封装
#继承--->单继承:是所有面对对象都支持的
class 另外一个类名():pass
class 类名(另外一个类名):pass
#多继承(可以继承多个类):是python特有的
class 另类1():pass
class 另类2():pass
class 类名(另类1,另类2):pass
class 另外一个类名():pass class 类名(另外一个类名):pass print(类名.__bases__) #bases是个属性 可以查看父类 print(另外一个类名.__bases__)#会打印一个‘object’,也就是object是所有对象的父类
#继承:就是父类有的东西,子类都可以使用
#继承与抽象的关系:先抽象再继承,抽象 比如麦兜是对象 抽象成猪 然而猪和人在抽象 为动物
#继承的作用:代码的重用性以及两种继承方法:
比如前面的人狗大战,很明显里面有几乎相同的代码 比如 name hp ad等 ,这是可以优化的如下
class Person: def __init__(self,name,hp,ad): self.name=name self.hp=hp self.ad=ad def attack(self,dog): #攻击 print('%s攻击了%s'%(self.name,dog.name)) dog.hp -= self.ad class Dog: def __init__(self,name,hp,ad): self.name=name self.hp=hp self.ad=ad def bite(self,person): person.hp -= self.ad
代码优化:将人和狗两个类相同的属性 都放在父类里面
class Role: def __init__(self,name,hp,ad): self.name=name self.hp=hp self.ad=ad class Person(Role): def attack(self,dog): #攻击 print('%s攻击了%s'%(self.name,dog.name)) dog.hp -= self.ad class Dog(Role): def bite(self,person): person.hp -= self.ad alex = Person('alex',100,1)
那如果父类的属性,并不是我子类全部的属性,该怎么办呢
class Role: def __init__(self,name,hp,ad): print('in Role init') self.name = name self.hp=hp self.ad=ad class Person(Role): def __init__(self,name,sex,hp,ad): self.sex = sex #派生属性(父类没有的) #子类没有的继承父类的,有下面两种方法: #Role.__init__(self,name,hp,ad) #指名道姓--继承 #super().__init__(name,hp,ad) #super()直接代表父类,不用传self super(Person,self).__init__(name,hp,ad) #super中有默认参数写不写都可以 def attack(self,dog): #派生方法(父类没有的) dog.hp -= self.ad class Dog(Role): def bite(self,person): person.hp -= self.ad alex = Person('alex',None,100,1) print(alex.__dict__)
#如果我自己的类里面也有一个init 先执行类里面的init
#继承的语法:在子类定义的时候括号里加上父类的名字
#对象在寻找名字的时候,先找自己对象空间里的-->再找自己的类的空间里的-->再找父类空间的
#在自己的类中调用父类中的同名方法:
1)指名道姓(需要自己传self参数)
2)super方法(不需要自己传self参数)
检测继承关系学的怎么样的一个方法:
class Foo: def __init__(self): #父类中的self是下面Foo类中的self,可以这样理解 self.func() def func(self): print('in foo') class Son(Foo): def func(self): print('in son') Son()
*************************************************************************************************************************************
组合:什么有什么的关系
一个类的对象作为另外一个类对象的属性
继承:什么是什么的关系
节省两个类之间共有的方法或者代码
如果子类和父类一样 那么可以省略子类的 用父类的
如果是子类特有的,用子类的---派生
如果两个方法,子类也有父类也有,用子类的
如果两个方法,子类也有父类也有,想用父类的:指名道姓,super
************************************************************************************************************************************
#抽象类和接口类---了解
#钻石继承--新式类 和 经典类--(python程序员需要了解的)。
在python中抽象类和接口类没有明显区别,主要就是维持了一种规范
python中 一切皆对象
dic = {'k':'v'} lst = [1,2,3] #这就是一个对象 lst dic 都是对象 len(dic) print(dic.__len__(),len(dic)) #这两个的值是一样的,len是函数,dic是对象,是dic先有了一个__len__方法,所以才有len(dic)这个功能 def len(dic):#len是函数 return dic.__len__()
list.append() #其实list就是个类,append是个方法
********************************************接口类**********************************************
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Wechatpay: def pay(self,money): print('微信支付了%s元' %money) class Alipay: def pay(self,money): print('支付宝支付了%s元' %money) class Applepay: #有位程序员新增一个付钱方式 def fuqian(self,money): #里面的fuqian必须是pay才行 print('apple支付了%s元' %money) def payment(obj,money):#用函数来代替两者支付方式 obj.pay(money) wp = Wechatpay() #wp.pay(100) #这一这样支付 也可以采取下面的支付方式,只是一种习惯而已 payment(wp,100) #编程习惯---归一化设计 ap = Alipay() #ap.pay(100) payment(ap,100) app = Applepay() #会报错 因为Applepay里面的方法是fuqian 不是pay payment(app,100)
对于上面出现的问题,有种解决办法 当然要知道上面出问题是因为没有规范的原因,导致付钱方式不同导致错误
from abc import ABCMeta,abstractmethod #记住这个 class Pay(metaclass=ABCMeta):#在类的位置指定metaclass = ABCMeta @abstractmethod #在指定的方法上面添加@abstractmenthod def pay(self,money):pass #目的是为了规范,这里不用怎么写 就是限制一下 #抽象类: #在类的位置指定metaclass =ABCMeta #在指定的方法上面添加@abstractmethod 装饰器 #抽象类的目的:规范所有继承这个类的子类,必须实现被@abstractmethod 装饰器 装饰的这个方法 #特点:抽象类和接口类不能被实例化,两者在python里没有什么区别 from abc import ABCMeta,abstractmethod class Pay(metaclass=ABCMeta): @abstractmethod def pay(self,money):pass #规范 class Wechatpay(Pay): def pay(self,money): print('微信支付了%s元'%money) class Alipay(Pay): def pay(self,money): print('支付宝支付了%s元'%money) class Applepay(Pay): def fuqian(self,money): print('apple支付了%s元'%money) def payment(obj,money): obj.pay(money) app = Applepay()#在这里就会报错,因为你用的支付方法是 'fuqian',所以必须改为pay
接口类:python天生支持多继承 java语言是不支持多继承的
python支持多继承,对于python来说抽象类和接口类没有区别
接口类是python特有的,因为python直接用类就可以实现接口的效果
python没有‘接口’这种数据类型,Java中有
from abc import ABCMeta,abstractmethod class Walk_Animal(metaclass=ABCMeta): #接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该用那些不需要的接口 @abstractmethod def walk(self):pass class Fly_Animal(metaclass=ABCMeta): @abstractmethod def fly(self):pass class Swim_Animal(metaclass=ABCMeta): @abstractmethod def swim(self):pass class Tiger(Walk_Animal,Swim_Animal):#老虎只会走和游泳,所以用不着飞的规范 def walk(self): print('walk') def swim(self): print('swim') class Parrot: def walk(self): print('walk') def fly(self): print('fly') class Swan: def walk(self): print('walk') def fly(self): print('fly') def swim(self): print('swim')
依赖倒置原则:
高层 模块不应该依赖底层模块,二者 都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象,换言之,要针对接口编程,而不是针对实现编程。
#抽象类和接口类:
#只能被继承不能被实例化
#子类必须实现父类中的同名方法--规范代码
#metaclass=ABCMeta @abstractmethod
#python支持多继承,对于python来说抽象类和接口类没有区别
#接口类是python特有的,因为python直接用类就可以实现接口的效果
#python没有‘接口’这种数据类型,Java中有
**************************************************************************抽象类****************************************************************************
什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
在python中实现抽象类
#一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 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)
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念
************************************************************************************************************
钻石继承 :4个类图形(也可以增加)就是形状像钻石 其实就是多继承
#D-B-C-A
#找一个名字,如果这个名字在子类不存在,就会遵循一个顺序往上找
#继承关系会形成一张图,每一个类都是这个图中的节点,寻找顺序会把图中的每个节点都找一遍且只找一次
------遍历算法
python3里面的所有类都是新式类
新式类的遍历算法遵循---广度优先规律
class A: def func(self):print('in a') class B(A):pass #def func(self):print('in b') class C(A): def func(self):print('in c') class D(B,C):pass #def func(self):print('in d') d = D() d.func()
另一种集成关系图
class E: def func(self):print('in E') class F: def func(self):print('in F') class B(E):pass #def func(self):print('in B') class C(F): def func(self):print('in C') class D(B,C):pass #当D子类里面没有func()的话,就会从父类B,C中找,那么这两个哪个先呢 先从做左边开始及B开始找 #def func(self):print('in D') d = D() d.func() #算法:路程相对最短,且最合理的
广度优先:先横着找,然后再竖着找 这种方法最优:
广度优先的走法:找下一个点的时候,首先往深度走,但是如果这个深度的类以后还会有机找到,那么就从广度找
深度优先和广度优先的走法对比,都是遍历每个节点,但是广度优先的步骤最少。
广度优先的理解图:在走道B之后会回来走C 为什么不走E呢,因为从C处还可以再找到E。
class E: def func(self):print('in E') class F: def func(self):print('in F') class B(E):pass #def func(self):print('in B') class C(F): def func(self):print('in C') class D(B,C):pass #def func(self):print('in D') print(D.mro()) #可以打印出继承路线
super并不是单纯的找父类,而是和mro顺序完全对应的
#super并不是单纯的找父类,而是和mro顺序完全对应的 class A: def func(self): print('A') class B(A): def func(self): print('B') super().func() class C(A): def func(self): print('C') super().func() class D(B,C): def func(self): print('D') super().func() d= D() d.func() #打印出D-B-C-A 遵循D.mro() B().func() #遵循B.mro(),打印B-A
新式类:python3里都是新式类,新式类默认继承object
经典类:python2里面既有经典类又有新式类。
新式类,主动继承object.
经典类:不主动继承object ----遵循深度优先遍历算法
在python2里面这种是经典类 就看父类 是否继承object class A: def func(self): print('A') class B(A): def func(self): print('B') super().func() class C(A): def func(self): print('C') super().func() class D(B,C): def func(self): print('D') super().func() 在python2里面这种是新式类 class A(object): def func(self): print('A') class B(A): def func(self): print('B') super().func() class C(A): def func(self): print('C') super().func() class D(B,C): def func(self): print('D') super().func()
******************************************************************************多态************************************************************************
python自带多态 Java是通过继承来实现
多态指的是一类事物有多种形态
动物有多种形态:人,狗,猪
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')
多态性
一 什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)
多态性是指在不考虑实例类型的情况下使用实例
在面向对象方法中一般是这样表述多态性:
向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
******************************************************************************封装************************************************************************
【封装】
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问
封装:在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
私有变量
私有的对象属性
私有的方法
私有的静态属性
class A: __静态属性 = 'aaa' #私有的静态属性 print(__静态属性)#这个什么时候执行? 直接就执行。为什么能执行?在遇到这种私有的时候会自动将 __静态属性变成 _类名__静态属性 #在一个变量之前,加上两个双下划线是有特殊意义的, #加上了这个双下划线,这个变量就成了私有的print(A.__静态属性) 没办法调用,为什么?私有的名字不能再类的外部使用 print(A.__dict__) print(A._A__静态属性) #这样可以调用,从语法的角度上,不允许直接调用的。 #从外部是否可以添加一个私有属性?:在一个类的外部是不可能定义一个私有属性的。 A.__wahaha = 'hahaha' print(A.__dict__) #查看类的属性
举例说明
#其实这仅仅这是一种变形操作 #类中所有双下划线开头的名称如__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,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形。
私有的对象属性
#私有的对象属性 class Room: def __init__(self,owner,id,length,width,height): self.owner =owner self.id=id self.__length =length #将属性设置成私有的 self.__width =width self.__height =height def area(self): return self.__length*self.__width r = Room('龙',302,2,1.5,1) print(r.area())
私有的方法:在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#私有的方法: #不希望从外部去调用这个方法,不独立完成一个功能,而是类整体完成某个功能的一部分 class Student: #对密码进行加密 def __init__(self,name,pwd): self.name=name self.__pwd =pwd def __getpwd(self): #在这假设对密码进行了加密 return self.__pwd[::-1] def login(self): self.__getpwd()
子类父类继承关系测试:
class A: def __init__(self): self.func() def func(self): print('A') class B(A): def func(self): print('B') B().func() #这里打印的是’B‘,要看self指向谁,指向的是B(),那么要用B类里面的方法
如果将上面的改成封装的,那么会打印什么呢?
class A: def __init__(self): self.__func() def __func(self): print('A') class B(A): def __func(self): print('B') B()
名字:
公有的:在类的外部,内部,子类用
_私有的:只能在类的内部中用
#下面的情况,子类能继承父类的私有方法吗? class A: def __init__(self): self.__func() def __func(self): #_A__func print('A') class B(A): #def __func(self): # print('B') def __init__(self): self.__func() #_B__func B() #会报错 原因是私有方法会变形,找不到
封装与扩展性
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积 return self.__width * self.__length #使用者 >>> r1=Room('卧室','egon',20,20,20) >>> r1.tell_area() #使用者调用接口tell_area #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了 return self.__width * self.__length * self.__high #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能 >>> r1.tell_area()
**************************************************************************几个装饰器函数***********************************************************************
第一个:property:将一个方法伪装成属性
from math import pi class Circle: def __init__(self,r): self.r = r def area(self): self.r**2*pi def perimeter(self): return 2*pi*self.r c = Circle(5) c.r c.area() #在这里我们理解的面积,周长应该都是属性,但他这里是方法 c.perimeter()#怎样能够使用功能c.area 就能直接调用属性,得出面积 #加上装饰器property后,就可以,如下面 from math import pi class Circle: def __init__(self,r): self.r = r @property def area(self): self.r**2*pi def perimeter(self): return 2*pi*self.r c = Circle(5) c.r print(c.area) #==> c.area()
#怎样更改私有属性
class Person: def __init__(self,name): self.__name=name #name是私有的 @property def name(self):#但是在外面想调用 于是用装饰器,伪装一个 return self.__name alex = Person('alex') print(alex.name) 但是我想改name那怎么改呢 class Person: def __init__(self,name): self.__name=name @property def name(self): return self.__name @name.setter #这个那么是被property装饰的name def name(self,new):#在定义一个name方法,需要两个参数 print(new) alex = Person('alex') print(alex.name) #对.name赋值,那怎么改呢,如上面 alex.name=23545 #能不能改?--不能直接改在外部更改内部私有属性 class Person: def __init__(self,name): self.__name=name @property def name(self): return self.__name @name.setter #这个那么是被property装饰的name def name(self,new):#在定义一个name方法,需要两个参数 #self.__name= 'long' #这样就可以改 if type(new) is str: #名字必须是字符串 self.__name = new #这样可以灵活的更改 alex = Person('alex') print(alex.name) #对.name赋值,那怎么改呢,如上面 alex.name='long'
上面例子解释:
再举一个例子来说明这个装饰器的作用
class Demo: @property def wahaha(self): print('in wahaha') return 'wahaha' Demo().wahaha #这样就可以把方法伪装成静态属性了 但是这样还不行,你可以更改赋值,所以 对私有方法进行更改 class Demo: def __init__(self,wahaha): self.__wahaha=wahaha @property def wahaha(self): print('in wahaha') return 'wahaha' @wahaha.setter #setter就相当于下面赋值的等号 def wahaha(self,new):#new是要赋的赋值 print('执行setter方法了',new) self.__wahaha = new d = Demo('wahaha') print(d.wahaha) #属性可以被查看 d.wahaha=123 #属性可以被更改 print(d.wahaha)
再举个实例
class Goods: def __init__(self,discount,origin_price): self.__price = origin_price self.__discount = discount @property def price(self): return round(self.__price*self.__discount,2) @price.setter def price(self,new_price): self.__price = new_price apple = Goods(0.8,10) print(apple.price) apple.price = 12#调整苹果的原价 print(apple.price)
#一个属性
#可以被查看
#可以被修改
#可以被删除吗?
属性是可以被删除的 class A:pass a = A() a.name = 'egon' print(a.__dict__) del a.name #删除属性 print(a.__dict__)
用装饰器删除举例
class A: def __init__(self,path): self.f = open(path,'w') def write(self,content): self.f.write(content) def close(self): self.f.close() obj = A('wahaha') obj.write('wahahaha') obj.close() del obj.f #f变量删除,但是文件没有关 #为了优化上面的代码 在删除变量之前 关闭文件 class A: def __init__(self,path): self.__f = open(path,'w') @property def f(self): return self.__f @f.deleter def f(self): #所有的借用操作系统的资源,在删除一个变量之前,都必须先归还资源 self.close() del self.__f def write(self,content): self.__f.write(content) def close(self): self.__f.close() obj = A('wahaha') obj.write('wahahaha') obj.close() del obj.f print(obj.f)
#method 方法-函数 由实例化对象去调用
#property 伪装成属性的方法--特性 由实例化对象去调用
#classmethod 类方法 由类调用,只使用类中的静态变量
#staticmethod 静态方法 由类调用,一个方法即不会用到对象的属性,也不会用到类的属性
class Goods: __discount = 0.8 #全场商品0.8折 def __init__(self,origin_price): self.__price = origin_price @property def price(self): return round(self.__price*Goods.__discount,2) @price.setter def price(self,new_price): self.__price = new_price def change_discount(self,new_discount):#这里面的self没有用,如果用到类的静态属性,但没有用,该怎么办 Goods.__discount = new_discount #店庆结束,恢复价钱 apple = Goods(10) apple.change_discount(1) #这里是改静态属性的值,但他是静态属性,不实例化应该也可以调用 print(apple.price) 进行更改 class Goods: __discount = 0.8 def __init__(self,origin_price): self.__price = origin_price @property def price(self): return round(self.__price*Goods.__discount,2) @price.setter def price(self,new_price): self.__price = new_price @classmethod #将一个方法装饰成类方法 def change_discount(cls,new_discount):#自动回传个类进来cls cls和Goods地址是一样的,是同一个 Goods.__discount = new_discount #这里就不需要实例化了 Good.change_discount(0.7) apple = Goods(10) print(apple.price)
staticmethod 静态方法 由类调用,一个方法即不会用到对象的属性,也不会用到类的属性
class Student: def __init__(self,name,sex): self.name=name self.sex=sex @staticmethod #相当于函数 def login(): name = input('>>') if name=='alex': print('登录成功') Student.login()
反射:
#如何输入一个函数名,得到对应的值 name1='alex' name2='egon' name3='guangtou' #inp = input('>>>') #print(inp) #四个内置函数 #hasattr #非常重要 #getattr #非常重要 #setattr #delattr class A: Name1 = 'alex' Name2 = 'egon' Name3= 'guangtou' def __init__(self,name): self.name=name def func(self): print('in func',self.name) #inp = input('>>>') print(hasattr(A,'Name')) #getattr的好伴侣 打印false print(hasattr(A,'Name1')) #打印True,所以可以用hassttr来辅助getattr避免没有值,而报错 if hasattr(A,'Name'):#用hasattr判断 print(getattr(A,'Name')) print(getattr(A,'Name1')) #A.Name1 print(getattr(A,'Name2')) #A.Name2 a1 = A('wusir') a1.func() print(a1.name) getattr(a1,'func')() #print(getattr(a1,'name')) #使用字符串数据类型的变量名,来操作一个变量的值 #使用反射获取某个命名空间中的值,需要 #有一个变量指向这个命名空间 #字符串数据类型的名字 #--将这两个传入,使用getattr取值, #如果是变量能直接获得结果 #如果是函数只能拿到内存地址,加上括号就能执行 #使用类名反射:静态属性,类方法,静态方法 #使用对象名反射:对象属性,类中的普通方法 #使用模块名反射 :变量 函数 类
#在自己所在的文件中反射全局变量:getatrr( sys.modules[__name__],'要反射的名字')
举例说明:
class A: Name1 = 'alex' Name2 = 'egon' Name3= 'guangtou' def __init__(self,name): self.name=name def func(self): print('in func',self.name) while True: inp = input('>>>') if hasattr(A,inp): print(getattr(A,inp))
这个需要两个模块来理解:
#使用模块名反射 :变量 函数 类 import mmm #在这个模块里引用mmm模块 #假设这个地方所在的文件是‘反射’ print(mmm.a) print(getattr(mmm,'a')) getattr(mmm,'wahaha')() taibai = mmm.QQxing('光头') taibai.ADCa() cls = getattr(mmm,'QQxing') #拿到mmm.QQxing obj = cls('光头')#对象 #print(obj) getattr(obj,'ADCa')()
模块‘mmm’
a =1 b =2 def wahaha(): print('wahaha') class QQxing: def __init__(self,name): self.name=name def ADCa(self): print('in ADCa',self.name) #如何在自己文件里使用模块名反射 import sys #print(sys.modules)#这样会打印出模块‘mmm’所对应的键__main__,但是如果在‘反射’模块里,打印的是反射所对应的__main__ #print(sys.modules['__main__']) #所有的模块都会存到这个modules里面 #print(getattr(sys.modules['__main__'],'a'))#但这样有个问题,如果是在其他模块里有反射引用,因为__main__不一致而会报错 print(getattr(sys.modules[__name__],'a'))#这样就万无一失了 在其他地方引用这个模块 执行它也不会有问题
setattr增改以及delattr删除
class A: name1= 'aelx' name2= 'egon' name3='guangtou' def __index__(self,name): self.name=name def func(self): print('in func',self.name) A.Country = '中国' #增 A.name1 = 'alex_dd'#改 #setattr :如果一个变量在的我就改,不在的话 我就增。 #print(A.__dict__ ) #setattr(A,'Country','印度') #print(A.__dict__ ) #setattr(A,'Role','Person') #print(A.__dict__ ) delattr(A,'name1') print(A.__dict__ )
补充两个内置函数:isinstance,issubclass
a = 10 print(isinstance(a,int)) class F:pass class Foo(F):pass obj = Foo() print(isinstance(obj,Foo)) print(type(obj)is Foo) print(isinstance(obj,F))#isinstance可以判断父类子类的关系 print(type(obj)is F)#type无法判断 class F:pass class Foo(F):pass print(issubclass(Foo,F))#判断一个类是不是这个类的子类,子类在前,父类在后。
类内置方法(双下滑方法,又叫魔术方法):
#__call__ #对象加上(),可以触发这个类的__call__方法。 class Foo: def __call__(self): print('call') obj = Foo() obj()#相当于调用__call__ 打印call #内置函数的len函数是依赖类中的__len__ class Classes: def __init__(self,student_lst): self.student_lst = student_lst def __len__(self):return len(self.student_lst) clas = Classes(['张三','程咬金','程咬银','程咬铁']) print(len(clas.student_lst)) print(len(clas)) #和上面得到的结果一样 class Wahaha: def __init__(self,num,name,age): self.num = num self.name=name self.age=age def __len__(self): return len(self.__dict__) #某个类中有多少属性? obj = Wahaha(6,'haha',7) print(len(obj.__dict__)) print(len(obj))
再一个
#__eq__ 是判断值是否相等的时候依赖__eq__的结果 class QQxing:pass q1 = QQxing() q2 = QQxing() print(q1,q2) print(q1 == q2) #两个自定义对象之间值是否相等,会根据内存地址来判断,所以是false #通过定义eq来实现只要两个对象的属性一样就相等 class QQxing: def __eq__(self,other): if type(self) == type(other) and self.__dict__==other.__dict__: return True else:return False q1 = QQxing() q1.name='alex' q2 = QQxing() q2.name = 'alex' print(q1 == q2) #这时候相等 #Person class #三个属性 name age sex #两个对象的 name和sex相同,name这两个对象就是相同的对象 #对100个对象去重 class Person: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __eq__(self,other): if type(self)==type(other) and \ self.name == other.name and \ self.sex == other.sex: return True else: return False a = Person('alex',18,'女') b = Person('alex',30,'女') print(a == b) #hash() -内存优化上起来作用 #Person class #三个属性 name age sex #两个对象的 name和sex相同,name这两个对象就是相同的对象 #对100个对象去重 class Person: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __hash__(self): return hash(self.name+self.sex) def __eq__(self, other): if type(self) == type(other) and \ self.name == other.name and \ self.sex == other.sex: return True else: return False person_lst = [Person('egon',i,'male') for i in range(100)] print(set(person_lst)) #set去重依靠的是内置hash值与eq值来去重。 print(hash(person_lst[1])) #让它一类中的hash为判断依据 print(hash(person_lst[2])) #得到的hash值都为1 #如果有个内置的方法和内置的函数同名,一定是在这个函数里调用了这个内置方法
set集合去重原理:
加入__str__方法
#__str__内置函数 #怎样用对象能直接读取出数值 l =[1,2,3] #实例化 a = 'aaa' print(l) class QQxing:pass q1 = QQxing() print(q1) class Course: def __init__(self,name,price,period): self.name=name self.price=price self.period =period courses = [] python = Course('python',23000,'6 months') linux = Course('linux',18000,'6 months') courses.append(python) courses.append(linux) print(courses)#打印的是地址 for course in courses: print(course.name,course.price,course.period)
加入__str__方法:和str print %s 都是息息相关的
class Course: def __init__(self,name,price,period): self.name=name self.price=price self.period =period print(self.__dict__,123) def __str__(self):#必须返回一个str数据类型 return '|'.join([self.name,str(self.price),self.period]) #return self.name +str(self.price)+self.period courses = [] python = Course('python',23000,'6 months') linux = Course('linux',18000,'6 months') courses.append(python) courses.append(linux) print(courses)#打印的是地址 for course in courses: print(course) #能直接打印出对应值 #price(course.name,course.price,course.period)
__repr__方法:和 repr() %r 都是息息相关的,注意:repr可以代替str来实现str相同的功能,但是str不能代替repr的功能,所以下面print(stu1)可以直接打印
#在没有实现str内置的方法情况下,repr可以完全代替str的功能,但是反过来不行,在都存在的情况下,各自实现各自的功能。
class Student: def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age def __repr__(self): return self.name stu1 = Student('光头','male',40) print(stu1)#光头 print('student: %r'%stu1) print(repr(stu1))
如果repr和str都存在,那么久各用各的
class Student: def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age def __repr__(self): return self.name def __str__(self): return '|'.join([self.name,self.sex,str(self.age)]) stu1 = Student('光头','male',40) print('student: %r'%stu1) print('student: %s'%stu1) print(repr(stu1)) print(str(stu1))
__format__
#_*_coding:utf-8_*_ 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 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 str: ',str(s1)) print(s1) ''' str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd'))
辅助理解:
class A:pass a=A() o.name='a' o.addr = 'b' o.type = 'c' print('{obj.name}-{obj.addr}-{obj.type}'.format(obj=o)))
__del__
#析构方法 class A: def __init__(self): self.f = open('userinfo','a') def __del__(self): '''在删除一个对象之前做一些收尾工作''' self.f.close() print('删除一个对象的时候调用我') a = A() del a #删除一个对象的时候,如果内部存在__del__方法, #那么在删除一个对象之前先执行__del__方法中的代码
__new__
#object.__new__() 自己是无法创建一个对象的,但可以实现__new__方法 class A: def __init__(self):print('执行init方法了') def __new__(cls): #先执行new print('执行new方法了') return object.__new__(cls) #创造对象,将对象返回 a = A() #先执行__new__方法,创造出一个对象 #然后把创造出来的对象传递给__init__方法 #会把self自动的返回,被a接受
#元类 #有一个元类 在创建类 #type() 所有直接用class创建出来的类的元类都是type class A: def __init__(self):print('执行init方法了') def __new__(cls): #先执行new print('执行new方法了') return object.__new__(cls) #创造对象,将对象返回 a = A() print(type(a)) print(type(A)) #也就是对象a是由类A中的__new__方法实现的,而类A是由type实现的
metaclass
#metaclass 前面学的接口类中学习过 #class 类名(metaclass =元类名) class A(metaclass=type):#这里其实是默认的写成class A:也对 def __init__(self):print('执行init方法了') def __new__(cls): #先执行new print('执行new方法了') return object.__new__(cls) #创造对象,将对象返回 a = A() print(type(a)) print(type(A)) #咱们前面学习到的,所用的知识就是这块 from abc import ABCMate,abstractmethod class A(metaclass = ABCMate): @abstractmethod def pay(self): pass
__item__
#item系列--打辅助的功能 #实现了之后,对象[]去做一些事情了 #如 # lst= [1,2,3] # lst[0] #有一些内置模块中的内置方法 # 是依赖__getitm__方法的 #或者说是依赖item['a']这种调用方式的 class Item: def __getitem__(self, item): print('-->',item) return 'helo' item = Item() item['a']#item加[]可以调用类里的getitem的方法 item['a'] = 'b'#会报错 增加改的功能 class Item: def __getitem__(self, item): print('-->',item) return 'helo' def __setitem__(self, key, value): print(key,value) item = Item() item['a']#item加[]可以调用类里的getitem的方法 item['a'] = 'b' item['a'] 将两个关联起来,以及删除 class Item: def __getitem__(self, item): print('-->',item) print(self.__dict__) return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value print(key,value) def __delitem__(self, key): self.__dict__.pop(key) item = Item() #__dict__是对象中默认存在的 print(item.__dict__) #item['a']#item加[]可以调用类里的getitem的方法 item['a'] = 'b' print(item['a']) print(item.a) print(item.__dict__) #del item.a del item['a'] print(item.__dict__)
纸牌游戏
from collections import namedtuple Card = namedtuple('Card',['rank','suit'])#一旦创建,不再改变,且可以使用属性名直接访问值 #Card1 = Card('k','黑桃') #print(Card1) # print(card1.rank) class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') suits = ['红心','方板','梅花','黑桃'] def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] #这里是怎样在类空间里引用全局方法 def __len__(self): return len(self._cards) def __getitem__(self, item): return self._cards[item] deck = FranchDeck() #print(deck._cards[0]) print(deck[0])#使用对象直接操作 获得self._cards的值,所以需要getitem内置方法 from random import choice print(choice(deck)) #choice接受iterable,choice依赖__len__,__getitem__方法 print(choice(deck)) #伪装成choice(deck._cards)
实现洗牌
#内置方法就是联合 或者单独的应用成 内置函数,在类中 #实现洗牌 #普通方法 # from collections import namedtuple # Card = namedtuple('Card',['rank','suit'])#一旦创建,不再改变,且可以使用属性名直接访问值 # class FranchDeck: # ranks = [str(n) for n in range(2,11)] + list('JQKA') # suits = ['红心','方板','梅花','黑桃'] # def __init__(self): # self._cards = [Card(rank,suit) for rank in FranchDeck.ranks # for suit in FranchDeck.suits] #这里是怎样在类空间里引用全局方法 # # def __len__(self): # # return len(self._cards) # def __getitem__(self, item): # return self._cards[item] # # deck = FranchDeck() # from random import shuffle # shuffle(deck._cards) # print(deck._cards) #使用内置方法 from collections import namedtuple Card = namedtuple('Card',['rank','suit'])#一旦创建,不再改变,且可以使用属性名直接访问值 class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') suits = ['红心','方板','梅花','黑桃'] def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] def __len__(self): return len(self._cards) def __getitem__(self, item): return self._cards[item] def __setitem__(self, item,value): self._cards[item] = value deck = FranchDeck() from random import shuffle shuffle(deck) #用shuffle[deck]来实现,它依赖__len__,__getitem__,__setitem__ print(deck._cards)