python自动化开发-[第七天]-面向对象
今日概要:
1、继承
2、封装
3、多态与多态性
4、反射
5、绑定方法和非绑定方法
一、新式类和经典类的区别
大前提: 1.只有在python2中才分新式类和经典类,python3中统一都是新式类 2.新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类 3.所有类甭管是否显式声明父类,都有一个默认继承object父类 在python2中的区分 经典类: class 类名: pass 经典类: class 类名(父类): pass 在python3中,上述两种定义方式全都是新式类
二、类的两种作用:属性引用和实例化
例子:
class gailun: country = 'demaxiya' def __init__(self,name,attack_id=50,flood=100): self.name = name self.attack_id = attack_id self.flood = flood def attack(self,persen): self.flood -= persen.attack_id
1、属性引用(类名.属性)
class gailun: #定义一个英雄gailun类,不通玩家可以用他实例出自己的英雄 country = 'demaxiya' #定义一个类变量 def __init__(self,name,attack_id=50,flood=100): #定义一个初始化函数 self.name = name self.attack_id = attack_id self.flood = flood def attack(self,persen): #定义一个函数属性 self.flood -= persen.attack_id g = gailun('dragon') #创建了一个对象 print (g.country) #引用类的数据属性,该属性所有对象和实例共享 gailun.country #引用类的属性,该属性所有对象和实例共享 g.name = 'banana' #增加属性 del g.name #删除属性
2、实例化__init__,self
类名加括号就执行类的__init__函数的运行,可以用__init__来定义每一个实例的特征
g = gailun('德玛西亚')#就是在执行gailun.__init__(g,'德玛西亚'),然后执行__init__内的代码g.name=‘德玛西亚’等
一,类的属性有两种属性可以查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类 类名.__bases__# 类所有父类构成的元组 类名.__dict__# 类的字典属性,名称空间 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
3、对象/实例只有一种作用:属性作用
三、类的名称空间与对象/实例的名称空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
类有两种属性:数据属性和函数属性
类的数据属性是共享给所有对象的
>>> id(r1.camp) #本质就是在引用类的camp属性,二者id一样 4315241024 >>> id(Riven.camp) 4315241024
类的函数属性是绑定给所有对象的
>>> id(r1.attack) >>> id(Riven.attack) ''' r1.attack就是在执行Riven.attack的功能,python的class机制会将Riven的函数属性attack绑定给r1,r1相当于拿到了一个指针,指向Riven类的attack功能 除此之外r1.attack()会将r1传给attack的第一个参数 '''
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
练习:每次对象实例化一次,就计数一次,最后打印出总次数
class count(object): num = 0 #定义类变量 def __init__(self,name): self.name = name count.num +=1 g = count('alex') g1 = count('egon') print (count.num)
四、类的继承
1、继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
class ParentClass1: #定义父类 pass class ParentClass2: #定义子类 pass class SubClass1(ParentClass1): #继承ParentClass1类,派生出SubClass1子类 pass class SubClass2(ParentClass1, ParentClass2): #继承ParentClass1类,ParentClass2,派生出SubClass1子类 pass
2、查看当前类的继承 类名.__bases__查看当前类所有的继承, ****__base__只查看从左到右继承的第一个子类
#查看当前类的继承顺序 print(SubClass1.__bases__) print(SubClass2.__bases__) ''' 输出: (<class '__main__.ParentClass1'>,) (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>) '''
3、继承的好处:减少冗余代码
4、派生的定义:在子类定义新的属性,覆盖掉父类的属性,称为派生
例子:
class Animal: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def eat(self): print('eating') def talk(self): print('%s 正在叫' %self.name) class People(Animal): def __init__(self, name, age, sex,education): Animal.__init__(self,name,age,sex) self.education=education def talk(self): Animal.talk(self) print('%s say hello' %self.name) class Pig(Animal): pass class Dog(Animal): pass peo1=People('xiaoming',18,'male','小学肄业') #People.__init__ pig1=Pig('xiaohong',20,'female') dog1=Dog('xiaogang',30,'male') print(peo1.education) peo1.talk() pig1.talk() dog1.talk() ''' 输出: 小学肄业 xiaohong 正在叫 xiaohong say hello xiaoming 正在叫 xiaogang 正在叫 '''
5、继承查找顺序:
class Parent: def foo(self): print('Parent.foo') self.bar() #s.bar() def bar(self): print('Parent.bar') class Sub(Parent): def bar(self): print('Sub.bar') s=Sub() #实例化 s.foo() #s.foo 查看当前类里没有foo,就去继承的父类里去查找,父类里有foo,同时调用self.bar, 由于传入的self为sub实例化的对象,所以子类和父类同时存在相同的函数属性,优先查找子类的 ''' 输出 Parent.foo Sub.bar ''' class Parent: def foo(self): print('Parent.foo') self.bar() #s.bar() def bar(self): print('Parent.bar') class Sub(Parent): pass s=Sub() s.foo() #s.foo ''' 输出: Parent.foo Parent.bar '''
6、子类继承,修改父类属性
class Animal: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def eat(self): print('eating') def talk(self): print('%s 正在叫' % self.name) class People(Animal): def __init__(self, name, age, sex, education): Animal.__init__(self, name, age, sex) #继承父类的__init__功能 self.education = education def talk(self): Animal.talk(self) print('%s say hello' % self.name) class Pig(Animal): pass class Dog(Animal): pass peo1 = People('alex', 18, 'male', '小学肄业') # People.__init__ pig1 = Pig('wupeiqi', 20, 'female') dog1 = Dog('yuanhao', 30, 'male') print(isinstance(peo1,People)) print(isinstance(pig1,Pig)) print(isinstance(dog1,Dog)) print(isinstance(peo1,Animal)) print(isinstance(pig1,Animal)) print(isinstance(dog1,Animal)) ''' 输出: True True True True True True '''
7、继承反映的是一种什么是什么的关系,组合也可以解决代码冗余问题,但是组合反映是一种什么有什么的关系
组合例子:
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Teacher(People): def __init__(self,name,age,sex,salary): People.__init__(self,name,age,sex) self.salary=salary class Student(People): pass class Date: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day def tell(self): print('%s-%s-%s' % (self.year, self.mon, self.day)) class Teacher(People): def __init__(self, name, age, sex, salary, year, mon, day): self.name = name self.age = age self.sex = sex self.salary = salary self.birth = Date(year, mon, day) class Student(People): def __init__(self, name, age, sex, year, mon, day): self.name = name self.age = age self.sex = sex self.birth = Date(year, mon, day) t=Teacher('egon',18,'male',3000,1995,12,31) t.birth.tell() #组合的形成 ''' 输出 1995-12-31 '''
五、接口的定义
定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
java中接口的定义
=================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java /* * Java的Interface很好的体现了我们前面分析的接口的特征: * 1)是一组功能的集合,而不是一个功能 * 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作 * 3)接口只定义函数,但不涉及函数实现 * 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */ package com.oo.demo; public interface IAnimal { public void eat(); public void run(); public void sleep(); public void speak(); } =================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口 package com.oo.demo; public class Pig implements IAnimal{ //如下每个函数都需要详细实现 public void eat(){ System.out.println("Pig like to eat grass"); } public void run(){ System.out.println("Pig run: front legs, back legs"); } public void sleep(){ System.out.println("Pig sleep 16 hours every day"); } public void speak(){ System.out.println("Pig can not speak"); } } =================第三部分:Person2.java /* *实现了IAnimal的“人”,有几点说明一下: * 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样 * 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */ package com.oo.demo; public class Person2 implements IAnimal { public void eat(){ System.out.println("Person like to eat meat"); } public void run(){ System.out.println("Person run: left leg, right leg"); } public void sleep(){ System.out.println("Person sleep 8 hours every dat"); } public void speak(){ System.out.println("Hellow world, I am a person"); } } =================第四部分:Tester03.java package com.oo.demo; public class Tester03 { public static void main(String[] args) { System.out.println("===This is a person==="); IAnimal person = new Person2(); person.eat(); person.run(); person.sleep(); person.speak(); System.out.println("\n===This is a pig==="); IAnimal pig = new Pig(); pig.eat(); pig.run(); pig.sleep(); pig.speak(); } } java中的interface
继承的两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class File: def read(self): #定接口函数read raise TypeError('类型错误') #raise主动抛出异常 def write(self): #定义接口函数write raise TypeError('类型错误') class Txt(File): #文本,具体实现read和write def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(File): #磁盘,具体实现read和write def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(File): # def read(self): # print('进程数据的读取方法') # # def write(self): # print('进程数据的读取方法') def xie(self): pass def du(self): pass p=Process() p.read() t=Txt() p=Process() d=Sata() print(isinstance(t,File)) print(isinstance(p,File)) print(isinstance(d,File)) t.read() p.read() d.read()
2、为什么要用接口
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
五、抽象类
1 什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
2 为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的
例子:
import abc class File(metaclass=abc.ABCMeta):#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。 @abc.abstractmethod def read(self): #定接口函数read pass @abc.abstractmethod def write(self): #定义接口函数write pass class Process(File): def read(self): #将read名字修改,或者缺少这两个参数,就会报错 # print('进程数据的读取方法') pass def write(self): print('进程数据的读取方法') # def xie(self): # pass # # def du(self): # pass p=Process() p.read()
六、类的继承顺序
新式类:广度优先
经典类:深度优先
通过mro(),可以查看类的继承顺序
继承顺序:
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中才分新式类与经典类 继承顺序
***子类继承父类的方法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()出现的问题
#每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') A.__init__(self) class C(A): def __init__(self): print('C的构造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的构造方法') B.__init__(self) C.__init__(self) pass f1=D() print(D.__mro__) #python2中没有这个属性
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
解决例子:
#每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') super(B,self).__init__() class C(A): def __init__(self): print('C的构造方法') super(C,self).__init__() class D(B,C): def __init__(self): print('D的构造方法') super(D,self).__init__() f1=D() print(D.__mro__) #python2中没有这个属性
七、多态和多态性
1、多态是同一种事物的不同形态
2、多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同功能的函数
在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
3、多态性的优点:
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
2.增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
#!/usr/bin/python # -*- coding:utf-8 -*- #多态是同一种事物的多种形态 class Animal: def talk(self): print('正在叫') class People(Animal): def talk(self): print('say hello') class Pig(Animal): def talk(self): print('哼哼哼') class Dog(Animal): def talk(self): print('汪汪汪') class Cat(Animal): def talk(self): print('喵喵喵') peo1=People() pig1=Pig() dog1=Dog() cat1=Cat() #多态性 # peo1.talk() # dog1.talk() # pig1.talk() def func(x): x.talk() func(peo1) func(pig1) func(dog1) func(cat1)
八、封装
1、封装的定义:
1:封装数据的主要原因是:保护隐私
2:封装方法的主要原因是:隔离复杂度(快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了)
2、类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问
3、在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的形式访问到.
例子:
class People: __country='China' def __init__(self,name,age,sex): self.__name=name #self._People__name=name self.__age=age self.__sex=sex def tell_info(self): print('人的名字是:%s ,人的性别是:%s,人的年龄是:%s' %( self.__name, #p._People__name self.__age, self.__sex)) p=People('alex',18,'male') print(p.__dict__) p.tell_info() print(p.__name) p.__salary=3000 #__私有属性或者变量只在定义时候修改 print(p.__dict__) print(People.__dict__) People.__n=11111111111111111111111111 print(People.__dict__) print(People.__n)
例子:
class Parent: def foo(self): print('from parent.foo') self.__bar() #self._Parent__bar() def __bar(self): #_Parent__bar print('from parent.bar') class Sub(Parent): # def __bar(self): #_Sub__bar # print('from SUb.bar') pass s=Sub() s.foo() s._Parent__bar()
九、特性
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
例子:
1、求圆面积和圆的周长,人的体脂
#!/usr/bin/python # -*- coding:utf-8 -*- import math class round(object): def __init__(self,leght): self.leght = leght @property def zhouchang(self): return math.pi * self.leght @property def mianji(self): return math.pi * ((self.leght/2) ** 2) g = round(10) print(g.zhouchang) print (g.mianji) 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) a = People('alex',70,1.70) print (a.bmi)
2、python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现
class People: def __init__(self,name,permmission=False): self.__name=name self.permmission=permmission @property def name(self): return self.__name @name.setter def name(self,value): if not isinstance(value,str): #在设定之前进行类型检查 raise TypeError('名字必须是字符串类型') self.__name=value @name.deleter def name(self): if not self.permmission: raise PermissionError('不允许的操作') del self.__name p=People('egon') # print(p.name) # # p.name='egon666' # print(p.name) # # p.name=35357 p.permmission=True del p.name
十、绑定方法和非绑定方法
类中定义的函数分成两大类:
一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):
1. 绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制
类.boud_method(),自动将类当作第一个参数传入
(其实对象也可调用,但仍将类当作第一个参数传入)
2. 绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制
对象.boud_method(),自动将对象当作第一个参数传入
(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
二:非绑定方法:用staticmethod装饰器装饰的方法
1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
class Foo: def test1(self): pass @classmethod #只能类进行调用 def test2(cls): print(cls) @staticmethod #类和对象都可以调用 def test3(): pass f=Foo() print(f.test1) print(Foo.test2) print(Foo.test3) print(f.test3)
1、staticmethod
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
import hashlib import time class MySQL: def __init__(self,host,port): self.id=self.create_id() self.host=host self.port=port @staticmethod def create_id(): #就是一个普通工具 m=hashlib.md5(str(time.clock()).encode('utf-8')) return m.hexdigest() print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数 conn=MySQL('127.0.0.1',3306) print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数
2、classmethod
classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法
settings.py
HOST='127.0.0.1' PORT=3306
示例:
import settings class MySQL: def __init__(self,host,port): self.host=host self.port=port print('conneting...') @classmethod def from_conf(cls): return cls(settings.HOST,settings.PORT) #MySQL('127.0.0.1',3306) def select(self): #绑定到对象的方法 print(self) print('select function') # conn=MySQL('192.168.1.3',3306) # conn.select() # conn1=MySQL('192.168.1.3',3306) conn2=MySQL.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类
3、classmethod和staticmethod区别
import settings class MySQL: def __init__(self,host,port): self.host=host self.port=port @staticmethod def from_conf(): return MySQL(settings.HOST,settings.PORT) # @classmethod # def from_conf(cls): # return cls(settings.HOST,settings.PORT) def __str__(self): return '就不告诉你' class Mariadb(MySQL): def __str__(self): return '主机:%s 端口:%s' %(self.host,self.port) m=Mariadb.from_conf() print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:
时间练习:
class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 @staticmethod def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) a=Date('1987',11,27) #自己定义时间 b=Date.now() #采用当前时间 c=Date.tomorrow() #采用明天的时间 print(a.year,a.month,a.day) print(b.year,b.month,b.day) print(c.year,c.month,c.day) #分割线============================== import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): t=time.localtime() return Date(t.tm_year,t.tm_mon,t.tm_mday) class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,但是结果为 ''' 输出结果: <__main__.Date object at 0x1013f9d68> ''' 因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day # @staticmethod # def now(): # t=time.localtime() # return Date(t.tm_year,t.tm_mon,t.tm_mday) @classmethod #改成类方法 def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化 class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿 ''' 输出结果: year:2017 month:3 day:3 '''
练习:
定义MySQL类
1.对象有id、host、port三个属性
2.定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一
3.提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化
4.为对象定制方法,save和get,save能自动将对象序列化到文件中,文件名为id号,文件路径为配置文件中DB_PATH;get方法用来从文件中反序列化出对象
class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 @staticmethod def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) a=Date('1987',11,27) #自己定义时间 b=Date.now() #采用当前时间 c=Date.tomorrow() #采用明天的时间 print(a.year,a.month,a.day) print(b.year,b.month,b.day) print(c.year,c.month,c.day) #分割线============================== import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): t=time.localtime() return Date(t.tm_year,t.tm_mon,t.tm_mday) class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,但是结果为 ''' 输出结果: <__main__.Date object at 0x1013f9d68> ''' 因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day # @staticmethod # def now(): # t=time.localtime() # return Date(t.tm_year,t.tm_mon,t.tm_mday) @classmethod #改成类方法 def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化 class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿 ''' 输出结果: year:2017 month:3 day:3 '''
十一、反射
1、python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
hasatter 判断object中有没有一个变量字符串对应的方法或属性
getatter 获取成员
setatter 设置成员
delatter 检查成员
例子1:
class Foo(object): def __init__(self): self.name = 'abc' def func(self): return '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) #abc delattr(obj,'name') print(obj.name) #报错
例子2:ftp的client
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
#!/usr/bin/python # -*- coding:utf-8 -*- class FtpClient: 'ftp客户端,但是还么有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr def get(self): print('is getting')
使用ftp的client
import ftpclient f1=ftpclient.FtpClient('192.168.1.1') if hasattr(f1,'get'): func=getattr(f1,'get') func() print('其他的代码1') print('其他的代码2') print('其他的代码3')