Python自动化开发课堂笔记【Day07】 - Python进阶(类)- 02
类与对象
对象是特征(变量)与技能(函数)的结合体,类是一系列对象共有的特征与技能的结合体
现实生活中:先有对象,再总结归纳出类
程序中:一定是先定义类,再实例化出对象
定义类的语法:
class 类名:
'''注释'''
类体(可以是任意代码)
1 class Chinese: 2 country = 'China' 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 print('--->',self.name,self.age) 7 def talk(self): 8 print('say Chinese')
1. 类的第一种用法,实例化
1 p1 = Chinese('Albert',18) 2 p2 = Chinese('Baker',33)
2. 类的第二种用法,属性引用
1 print(Chinese.country)#类的数据属性 2 print(Chinese.__init__)#类的函数属性 3 p1.__init__('Talbert',81)
3. 其他知识点补充
1 print(Chinese.__dict__)#查看类的属性字典,或者说是名称空间 2 #{'__module__': '__main__', '__doc__': None, 'talk': <function Chinese.talk at 0x00675D68>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, 'country': 'China', '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__init__': <function Chinese.__init__ at 0x00675228>} 3 print(Chinese.country) 4 print(Chinese.__dict__['country'])#效果同上,本质上是如何取到的country这个变量 5 print(p1.name,p1.age) 6 print(p1.__dict__['name'],p1.__dict__['age']) 7 8 #类型和类是统一的 9 print(type(p1)) #<class '__main__.Chinese'> 10 11 #类中定义的变量对象是公用的,不会产生新的内存空间 12 print(id(p1.country)) #5593664 13 print(id(p2.country)) #5593664 14 print(id(Chinese.country)) #5593664
类绑定方法(类实例化后绑定到对象身上)
绑定方法:绑定到谁身上就是给谁用的,谁来调用就会自动把自己当作第一个参数传入
1 print(Chinese.talk) #<function Chinese.talk at 0x006F5D68> 2 print(p1.talk) #<bound method Chinese.talk of <__main__.Chinese object at 0x002759F0>> 3 print(p2.talk) #<bound method Chinese.talk of <__main__.Chinese object at 0x0070EB50>>
总结:定义在类内部的变量是所有对象共有的,id全一样。定义在类内部的函数,是绑定到所有对象的,是给对象来用的,obj.func()会把obj本身当作第一个参数传入
print(p1.x) # p1.__dict__ ---> Chinese.__dict__ ---> 报错
如果要查找一个属性,先从对象名称空间中查找,找不到之后再到类名称空间查找,如果还找不到不会再去全局找,直接报错
练习:
1 1.统计类实例化对象的次数 2 class Chinese: 3 country = 'China' 4 count = 0 5 def __init__(self,name,age): 6 self.name = name 7 self.age = age 8 Chinese.count += 1 #类变量的概念 9 print('--->',self.name,self.age) 10 print('%d obj create' % Chinese.count) 11 p1=Chinese('A',1) 12 p2=Chinese('B',2) 13 14 2.定义一个学生类 15 class Stu: 16 def __init__(self,stuid,name,age): 17 self.stuid = stuid 18 self.name = name 19 self.age = age 20 print('Student--->',self.stuid,self.name,self.age) 21 s1 = Stu(1,'A',18) 22 s2 = Stu(2,'B',18) 23 24 3.类对象交互 25 class A: 26 camp = 'AAA' 27 def __init__(self,nickname,damage=100, HP=200): 28 self.nickname = nickname 29 self.damage = damage 30 self.HP = HP 31 def attack(self,enemy): 32 enemy.HP -= self.damage 33 print('造成%d点伤害' % self.damage) 34 class B: 35 camp = 'BBB' 36 def __init__(self,nickname,damage=200, HP=100): 37 self.nickname = nickname 38 self.damage = damage 39 self.HP = HP 40 def attack(self,enemy): 41 enemy.HP -= self.damage 42 print('造成%d点伤害' % self.damage) 43 a1 = A('a1') 44 b1 = B('b1') 45 print(a1.camp) 46 print(b1.camp) 47 a1.attack(b1) 48 b1.attack(a1)
继承
继承是一种创建新类的方式,新建的类可以继承一个或多个父类,父类又可以称为基类或超类,新建的类称为派生类或子类
1 class ParentClass1: 2 pass 3 class ParentClass2: 4 pass 5 class SubClass1(ParentClass1): 6 pass 7 class SubClass2(ParentClass1,ParentClass2): 8 pass
如何查看继承的父类
1 print(SubClass1.__bases__) #(<class '__main__.ParentClass1'>,) 2 print(SubClass2.__bases__) #(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
类的种类
1 #Python2中分为新式类和经典类 2 #新式类(有括号的,有继承关系的) 3 class Foo(object): 4 pass 5 #经典类(没括号的,谁都不继承) 6 class Bar: 7 pass 8 #Python3中全部都是经典类(即使没有括号也是新式类) 9 class Foo: 10 pass 11 print(Foo.__bases__) #(<class 'object'>,)
继承的好处:
1.减少冗余代码
2.在子类定义新的属性,覆盖掉父类的属性,称为派生
1 class Animal: 2 def __init__(self,name,age,sex): 3 self.name = name 4 self.age = age 5 self.sex =sex 6 def eat(self): 7 print('eating...') 8 def talk(self): 9 print('%s saying...' % self.name) 10 class People(Animal): 11 def __init__(self,name,age,sex,edu): 12 Animal.__init__(self,name,age,sex) 13 self.edu = edu 14 def talk(self): 15 Animal.talk(self) 16 print('%s say hello' % self.name) 17 class Pig(Animal): 18 pass 19 class Dog(Animal): 20 pass 21 p1 = People('p1',18,'male','college') 22 g1 = Pig('g1',11,'female') 23 d1 = Dog('d1',22,'male') 24 print(p1.edu) 25 p1.talk() 26 g1.talk() 27 d1.talk() 28 print(isinstance(p1,People)) 29 print(isinstance(g1,Pig)) 30 print(isinstance(d1,Dog))
对象如果要调用方法,先从对象自己的命名空间找,然后是自己的类,最后是父类,依次往上找
1 class Parent: 2 def foo(self): 3 print('Parent.foo') 4 self.bar() 5 def bar(self): 6 print('Parent.bar') 7 class Sub(Parent): 8 def bar(self): 9 print('Sub.bar') 10 s=Sub() 11 s.foo() # Parent.foo 12 # Sub.bar
继承反应的是一种什么是什么的关系
组合也可以解决代码冗余的问题,但是组合反应的是一种什么有什么的关系
1 class People: 2 def __init__(self,name,age,sex): 3 self.name=name 4 self.age=age 5 self.sex=sex 6 class Date: 7 def __init__(self,y,m,d): 8 self.y = y 9 self.m = m 10 self.d = d 11 def tell(self): 12 print('%s-%s-%s' % (self.y,self.m,self.d)) 13 class Teacher(People): 14 def __init__(self,name,age,sex,salary,y,m,d): 15 People.__init__(self,name,age,sex) 16 self.salary = salary 17 self.birth = Date(y,m,d) 18 19 class Student(People): 20 def __init__(self,name,age,sex,y,m,d): 21 People.__init__(self,name,age,sex) 22 self.birth = Date(y,m,d) 23 24 t1 = Teacher('A',18,'male',3000,2000,1,1) 25 t1.birth.tell()
抽象类
1 import abc 2 class File(metaclass=abc.ABCMeta): 3 @abc.abstractmethod 4 def read(self): 5 pass 6 @abc.abstractmethod 7 def write(self): 8 pass 9 class Txt(File): 10 def read(self): 11 pass 12 def write(self): 13 pass 14 p=Txt()
继承搜索顺序:
Python2中,分为
广度优先(新式类)F->D->B->A->E->C->H
深度优先(经典类)F->D->B->E->C->H->A
Python3中,只有广度优先(python3中只有新式类)
1 class A: 2 def test(self): print('from A') 3 pass 4 class B(A): 5 def test(self): print('from B') 6 pass 7 class C(A): 8 def test(self): print('from C') 9 pass 10 class D(B): 11 def test(self): print('from D') 12 pass 13 class E(C): 14 def test(self): print('from E') 15 pass 16 class H(A): 17 def test(self): print('from H') 18 pass 19 class F(D,E,H): 20 def test(self): print('from F') 21 pass 22 f=F() 23 f.test() 24 print(F.mro()) 25 #[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, 26 # <class '__main__.C'>, <class '__main__.H'>, <class '__main__.A'>, <class 'object'>]
子类中重用父类的方法(super)
1 class Foo: 2 def test(self): 3 print('from foo.test') 4 class Bar(Foo): 5 def test(self): 6 super().test() 7 print('from bar.test') 8 b = Bar() 9 b.test() 10 11 class Foo1: 12 def test(self): 13 print('from foo1.test') 14 class Foo2: 15 def test(self): 16 print('from foo2.test') 17 class Bar(Foo1,Foo2): 18 def test(self): 19 super().test() 20 print('from bar.test') 21 b = Bar() 22 b.test() #from foo1.test 23 #from bar.test
多态与多态性
没有多态就没有多态性
多态:同一种事物的多种形态
多态性:指的是具有不同功能的函数可以使用相同的函数名
1 class Animal: 2 def eat(self): 3 print('eating...') 4 class People(Animal): 5 def eat(self): 6 print('eating...') 7 class Pig(Animal): 8 def eat(self): 9 print('eating...') 10 class Dog(Animal): 11 def eat(self): 12 print('eating...') 13 p1=People() 14 g1=Pig() 15 d1=Dog() 16 def func(obj): 17 obj.eat() 18 func(p1) 19 func(g1) 20 func(d1)
封装
为什么要封装:保护隐私,隔离复杂度
Python没有真正的隐藏,只是从语法级别做了些改变
1 class Foo: 2 __x = 1 # '_Foo__x': 1 3 def __test(self): # '_Foo__test': <function Foo.__test at 0x00665D68> 4 print('from test') 5 print(Foo.__dict__) 6 print(Foo._Foo__x) 7 print(Foo._Foo__test) 8 9 class People: 10 __country = 'Chinese' 11 def __init__(self,name,age,sex): 12 self.__name=name #只在定义阶段才会发生变形,实例产生后新加入的变量就不会变形了 13 self.__age=age 14 self.__sex=age 15 def tell_info(self): 16 print('%s:%s:%s' % (self.__name,self.__age,self.__sex)) 17 p=People('alex',18,'male') 18 print(p.__dict__) #{'_People__age': 18, '_People__sex': 18, '_People__name': 'alex'} 19 p.tell_info() 20 p.__salary = 3000 21 print(p.__dict__) #{'_People__name': 'alex', '_People__sex': 18, '_People__age': 18, '__salary': 3000} 22 23 class People: 24 __country = 'Chinese' 25 def __init__(self,name,age): 26 self.__name=name 27 self.__age=age 28 def tell_info(self): 29 print('Name:%s,Age:%d' % (self.__name,self.__age)) 30 def set_info(self,x,y): 31 if not isinstance(x,str): 32 raise TypeError('must be str!!!') 33 if not isinstance(y,int): 34 raise TypeError('must be int!!!') 35 self.__name = x 36 self.__age = y 37 p1 = People('Albert',18) 38 p1.tell_info() # Name:Albert,Age:18 39 p1.set_info('Baker',22) 40 p1.tell_info() # Name:Baker,Age:22
关于property的应用
1 应用1: 2 class Foo: 3 @property 4 def test(self): 5 print('from foo') 6 # test = property(test) 7 f=Foo() 8 # f.test() #from foo 9 f.test #from foo 10 11 应用2: 12 class People: 13 def __init__(self,name,weight,height): 14 self.name = name 15 self.weight = weight 16 self.height = height 17 @property 18 def bmi(self): 19 return self.weight / (self.height ** 2) 20 p = People('Albert',75,1.80) 21 p.height=1.82 22 print(p.bmi) 23 24 应用3: 25 import math 26 class Circle: 27 def __init__(self,radius): 28 self.raduis = radius 29 @property 30 def zc(self): 31 return 2 * math.pi * self.raduis 32 c = Circle(5) 33 print(c.zc) 34 35 应用4: 36 class Person: 37 def __init__(self,name,permission=False): 38 self.__name = name 39 self.permission = permission 40 @property 41 def name(self): 42 return self.__name 43 @name.setter 44 def name(self,value): 45 if not isinstance(value,str): 46 raise TypeError('must be str') 47 self.__name = value 48 @name.deleter 49 def name(self): 50 if not self.permission: 51 raise PermissionError('not allowed') 52 del self.__name 53 p=Person('Albert') 54 print(p.name) 55 p.name='Baker' 56 print(p.name) 57 p.permission = True 58 del p.name
绑定
类中定义的函数分成两大类:
1.绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入)
1.绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制
类.bound_method(),自动将类当作第一个参数传入(其实对象也可调用,但仍将类当作第一个参数传入)
2.绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制
对象.bound_method(),自动将对象当作第一个参数传入(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值的说法)
2.非绑定方法:用staticmethod装饰器装饰的方法
不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说,就是一个普通工具而已
1 #绑定到类的方法 2 class Foo: 3 @classmethod 4 def test(cls): 5 print(cls) 6 Foo.test() #<class '__main__.Foo'> 7 #跟谁都不绑定的方法 8 class Foo: 9 @staticmethod 10 def test(x,y): 11 print('test',x,y) 12 Foo.test(1,3) 13 f=Foo() 14 f.test(2,4)
示例
1 #setting文件配置 2 # HOST = '127.0.0.1' 3 # PORT = 3306 4 import setting 5 import hashlib 6 import time 7 class MySQL: 8 def __init__(self,host,port): 9 self.sql_id = self.sql_id() 10 self.host = host 11 self.port = port 12 print('connecting...') 13 @classmethod 14 def from_conf(cls): 15 return cls(setting.HOST,setting.PORT) #相当于MySQL(host,port) 16 @staticmethod 17 def sql_id(): 18 m = hashlib.md5(str(time.clock()).encode('utf-8')) 19 return m.hexdigest() 20 def select(self): 21 print(self) #<__main__.MySQL object at 0x0060E2D0> 22 print('select func',self.host,self.port) 23 24 conn1 = MySQL('192.168.1.1',3306) 25 conn1.select() 26 print(conn1.sql_id) 27 conn2 = MySQL.from_conf() 28 print(conn2.sql_id)
staticmeth与classmethod的区别
1 class Person: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 def __str__(self): 6 print('run __str__') 7 return 'name:%s age:%s' % (self.name, self.age) 8 p = Person('Albert',19) 9 print(p) 10 11 #setting文件配置 12 # HOST = '127.0.0.1' 13 # PORT = 3306 14 import setting 15 class MySQL: 16 def __init__(self,host,port): 17 self.host = host 18 self.port = port 19 print('connecting...') 20 @classmethod 21 def from_conf(cls): 22 return cls(setting.HOST, setting.PORT) 23 def __str__(self): 24 return 'from MySQL' 25 class Mariadb(MySQL): 26 def __str__(self): 27 print(self.host,self.port) 28 return 'from Maria' 29 conn1 = Mariadb.from_conf() 30 print(conn1)
反射(自省)
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问,检测和修改它本身状态或行为的一种能力。
Python面向对象中的反射:通过字符串的形式操作对象相关的属性。Python中的一切皆对象(都可以使用反射)
1 class People: 2 country = 'China' 3 def __init__(self,name,age): 4 self.name=name 5 self.age=age 6 print(People.country) 7 p = People('Albert',18) 8 print(p) 9 print(hasattr(p,'name')) # True 10 print(hasattr(People,'country')) #True 11 setattr(p,'sex','male') 12 print(p.__dict__) # {'age': 18, 'name': 'Albert', 'sex': 'male'} 13 print(getattr(p,'nam','not exist')) # not exist 14 print(getattr(p,'name')) 15 # setattr(p,'x',1) 16 if hasattr(p,'x'): 17 res = getattr(p,'x') 18 print(res) 19 delattr(p,'sex') 20 print(p.__dict__)
练习
1 import sys 2 res = sys.modules[__name__] #获取当前模块 <module '__main__' from 'C:/Users/ajiax/PycharmProjects/Python17/Day07/ALesson07.py'> 3 print(res) 4 if hasattr(res,'People'): 5 get = getattr(res,'People') 6 print(get) 7 obj=get('Baker',22) 8 print(obj.name) 9 10 11 class FTPClient: 12 def __init__(self,host): 13 self.host=host 14 print('connecting...%s' % self.host) 15 def interactive(self): 16 while True: 17 cmd_line = input('>>>:').strip() 18 cmd_list = cmd_line.split() 19 if hasattr(self,cmd_list[0]): 20 func = getattr(self,cmd_list[0]) 21 func(cmd_list) 22 def get(self,args): 23 print('downloading...%s' % args[1]) 24 p = FTPClient('127.0.0.1') 25 p.interactive()