首页 > 代码库 > 18-面向对象之基础

18-面向对象之基础

目录:类和对象,多态与多态性,封装,绑定与非绑定方法,继承,反射
 
-------------------------------------------
面向过程:根据业务逻辑从上到下写垒代码
函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
面向对象:对函数进行分类和封装,让开发“更快更好更强...
 
面向过程编程
核心是过程,过程就是解决问题的步骤,即先做什么,在做什么,基于面向过程设计程序,好比流水线,是一种机械思维方法。
优点:复杂的问题简单化
缺点:可扩展性差,牵一发而动全身
应用场景: linux内核,httpd,git
 
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)
核心是对象,要理解对象应该把自己当作上帝,一切事务都是对象,不存在的也可创建出来。
 
对象(obj)
对象是特征(变量)与技能(函数)的结合体
优点:可扩展性强
缺点:无法像面向过程一样准确的知道什么阶段发生什么事,会有什么结果
应用场景:与用户层交互多的,公司内部的软件,游戏,互联网软件
 
类(class)
类是一些列对象共有的特征与技能的集合体
 
在程序中使用,需要先定义类,再定义对象,即实例化出对象
 
-------------------------------------------
 
-------------------------------------------
创建类和对象
 
面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用
--类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
--对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
类的语法:
 
  1. class 类名:
  2.     """
  3.     注释
  4.     """
  5.     类体(可是任意代码)
示例:
  1. # 定义一个类
  2. class Chinese:
  3. country = ‘China‘ # 属性
  4. def __init__(self, name, age): # 初始化,实例化出来的对象默认拥有name,age等属性
  5. self.name = name # p1.name=name
  6. self.age = age # p1.age=age
  7. def talk(self): # 技能
  8. print(‘say Chinese‘)
  9. p1 = Chinese(‘rain‘, 18) # 实例化出一个对象p1
  10. print(p1.country) # 类的数据属性
  11. print(p1.__init__) # 类的函数属性
  12. print(p1.talk()) # 类的函数属性,加括号即可运行
  13. print(p1.name)
  14. print(p1.age)
PS:
1,class是关键字,表示类,注意后面冒号‘:‘
2,创建对象,类名称后加括号即可
3,类中的函数,默认第一个参数都为为self,代表实例化对象自己,self.name相当于p1.name
4,整个函数的功能称作类的方法
5,定义在类内部的变量,是所有对象共有的,id全一样
6,定义在类内部的函数,是绑定到所有对象的,是给对象来用,obj.func()会把obj本身当做第一个参数出入
7,绑定方法:绑定到谁的身上,就是给谁用的,谁来调用就会自动把自己当作第一个参数传入
 
类的特殊方法:
__dict__:查看类的属性字典或类的名称空间
 
  1. print(Chinese.__dict__)
  2. 运行结果:(一个字典)
  3. {‘talk‘: <function Chinese.talk at 0x0000000000B4E268>, ‘country‘: ‘China‘, ‘__doc__‘: None, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__init__‘: <function Chinese.__init__ at 0x0000000000B4E1E0>, ‘__module__‘: ‘__main__‘, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>}
 
-------------------------------------------
 
 
-------------------------------------------
练习1:对象个数统计功能
每实例化一次,记一次数,最后统计一个类实例化了几个对象
  1. class Foo:
  2. count = 0 # 类的数据属性
  3. def __init__(self, name):
  4. Foo.count += 1
  5. self.name = name
  6. obj1 = Foo(‘rain‘)
  7. obj2 = Foo(‘tom‘)
  8. print(‘该类共实例化了 %s 个对象.‘ % Foo.count)
  9. 运行结果:
  10. 该类共实例化了 2 个对象.
 
 
练习2:对象间的交互
定义2个英雄人物,可以互相攻击,血量减少
  1. class Garen:
  2. camp = ‘Demacia‘ # 定义通用属性:阵营
  3.  
  4. def __init__(self, nickname, life_value=600, agg = 100):
  5. self.nickname = nickname
  6. self.life_value = life_value
  7. self.agg = agg
  8. def attack(self, enemy): # 定义攻击方法,攻击敌人
  9. enemy.life_value -= self.agg
  10. class Riven:
  11. camp = ‘Noxus‘
  12.  
  13. def __init__(self, nickname, life_value=400, agg = 150):
  14. self.nickname = nickname
  15. self.life_value = life_value
  16. self.agg = agg
  17. def attack(self, enemy):
  18. enemy.life_value -= self.agg
  19. g = Garen(‘德码‘)
  20. r = Riven(‘瑞文‘)
  21. print(g.life_value) # 查看血量值
  22. print(r.life_value)
  23. g.attack(r) # 模拟攻击
  24. print(r.life_value) # 攻击后血量值
  25.  
  26. r.attack(g)
  27. print(g.life_value)
 
-------------------------------------------
 
-------------------------------------------
 
继承
1,什么是继承:
继承是一种创建新类的方式,新建的类可以继承一个多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
 
格式:
 
  1. class ParentClass1:
  2.     pass
  3. class ParentClass2:
  4.     pass
  5. class SubClass1(ParentClass1):
  6.     """
  7.     继承父类1
  8.     """
  9.     pass
  10. class SubClass2(ParentClass1, ParentClass2):  # 调用时,会按照从左到右顺序查找
  11.     """
  12.     继承2个父类:父类1和父类2
  13.     """
  14.     pass
 
2,继承关系的寻找
先在对象自己里面找,没有再去对象的类中找,再没有去父类找
 
在Python3中可以使用
print(Foo.mro())
返回值是个列表:
列表元素固定,也就是继承查找的顺序
 
Python2中的类分为:新式类与经典类
  1. 新式类:
  2. class Foo(object):
  3.     pass
  4. 经典类:
  5. class Foo:
  6.     pass
  7. Python3中都为新式类
  8. class Foo:
  9.     pass
  10. print(Foo.__dict__())
 
 
 
 
例如:
关系图如下:
技术分享
F的父类是A,B,C;
A的父类是D;
B的父类是E;
定义如下类来测试:
  1. class E:
  2. def test(self):
  3. print(‘from E‘)
  4. pass
  5. class A(E):
  6. def test(self):
  7. print(‘from A‘)
  8. pass
  9. class D:
  10. def test(self):
  11. print(‘from D‘)
  12. pass
  13. class B(D):
  14. def test(self):
  15. print(‘from B‘)
  16. pass
  17. class C:
  18. def test(self):
  19. print(‘from C‘)
  20. pass
  21.  
  22. # 优先找F自己类中的test函数,F类中没有,找A-->D,D没有回来找B-->E,还没有找C
  23. class F(A, B, C):
  24. def test(self):
  25. print(‘from F‘)
  26. pass
  27. f = F()
  28. f.test()
如果是如下图形式:
技术分享
新式类查找顺序:广度优先
F-->D-->B-->E-->C-->A  (不回一找到底,找完分支后,最后找A)
经典类查找顺序:深度优先
F-->D-->B-->A-->E-->C
 
3,继承可以解决代码冗余问题,继承反映的是一种什么什么的关系
 
例如:
 
 
  1. class People: # 定义父类
  2.     def __init__(self, name, age, sex):
  3.         self.name = name
  4.         self.age = age
  5.         self.sex = sex
  6. class Teacher(People): # 子类继承父类,且有自己的独特的属性salary
  7.     def __init__(self, name, age, sex, salary):
  8.     People.__init__(self, name, age, sex)
  9.     self.salary = salary 
  10. class Student(People): # 子类继承父类
  11.     pass
  12. t = Teacher(‘rain‘, 19, ‘male‘, 3000)
  13. print(t.salary)
  14. s = Student(‘a‘, 10, ‘male‘)
  15. print(s.name)
  16. print(s.age)
  17. print(s.sex)
  18. 运行结果:
  19. 3000
  20. a
  21. 10
  22. male
 
supper().父类的方法名:允许子类重用父类中的方法
例如:
  1. class Foo:
  2. def test(self):
  3. print(‘from foo.test...‘)
  4. class Bar(Foo): # 如果有多个父类,按照从左到右顺序查找,print(Bar.mro())可查看查看顺序
  5. def test(self):
  6. super().test() # Foo.test(self)
  7. # super(Bar, self).test()
  8. print(‘from Bar...‘)
  9.  
  10. b = Bar()
  11. b.test()
  12. 运行结果:
  13. from foo.test...
  14. from Bar...
 
组合
组合也可以解决代码冗余问题,组合反映的是什么什么的关系
将不同的类组合起来相互调用
 
例如:
 
 
  1. class Date:
  2.     def __init__(self, year, mon, day):
  3.         self.year = year
  4.         self.mon = mon
  5.         self.day = day
  6.     def tell(self):
  7.         print(‘%s-%s-%s‘ % (self.year, self.mon, self.day))
  8. class People:
  9.     def __init__(self, name, age, sex):
  10.         self.name = name
  11.         self.age = age
  12.         self.sex = sex
  13. class Teacher(People):
  14.     def __init__(self, name, age, sex, salary, year, mon, day):
  15.     People.__init__(self, name, age, sex)
  16.     self.salary = salary
  17.     self.birth = Date(year, mon, day)
  18. class Student(People):
  19.     def __init__(self, name, age, sex, year, mon, day):
  20.         People.__init__(self, name, age, sex)
  21.         self.birth = Date(year, mon, day)
  22. t = Teacher(‘rain‘, 19, ‘male‘, 10000, 1990, 6,20)
  23. print(t.salary)
  24. t.birth.tell()
  25. s = Student(‘a‘, 11, ‘male‘, 2007, 12, 12)
  26. print(s.name)
  27. s.birth.tell()
  28. 运行结果:
  29. 1000
  30. 1990-6-20
  31. a
  32. 2007-12-12
 
组合应用场景:
学生类与学号类
学生类与学生学的课程类
教师类与教师教的课程类
 
多态和多态性
 
多态:同一种事物的多种形态,例如:
1,序列类型有多种形态:如字符串,列表,元组等
2,动物有多种形态:人,狗,猫
例如:动物有不同的形态:人, 狗, 猪, 猫,不管你是什么动物,只要知道动物都能发声,有talk方法即可,定义一个通用的接口func,来统一调方法即可。
 
  1. class Animal:
  2. def talk(self):
  3. print(‘发声...‘)
  4. class People(Animal):
  5. def talk(self):
  6. print(‘人在说话...‘)
  7. class Pig(Animal):
  8. def talk(self):
  9. print(‘猪在叫...‘)
  10. class Dog(Animal):
  11. def talk(self):
  12. print(‘狗在叫...‘)
  13. class Cat(Animal):
  14. def talk(self):
  15. print(‘猫在叫...‘)
  16. def func(obj):
  17. """
  18. 统一接口
  19. obj具有多态性,没有类型概念
  20. :param obj:
  21. :return:
  22. """
  23. obj.talk()
  24. peo1 = People()
  25. dog1 = Dog()
  26. pig1 = Pig()
  27. cat1 = Cat()
  28. # peo1.talk()
  29. # dog1.talk()
  30. # pig1.talk()
  31. func(peo1)
  32. func(dog1)
  33. func(pig1)
  34. func(cat1)
  35. 运行结果:
  36. 人在说话...
  37. 狗在叫...
  38. 猪在叫...
  39. 猫在叫...
 
封装
为啥要封装:
1,封装数据的主要原因是:保护隐私
2,封装方法的主要原因是:隔离复杂度
 
类中把某些属性和方法隐藏起来(或者说定义成私有的),仅在类的内部使用,外部无法访问,或者留下少量接口(函数)供外部访问.
在python中用双下划线的方式实现隐藏属性(即设置成私有的)
 
例如:__x
类中所有双下划线开头的名称如__x,都会会自动转换为_类名__x的形式:
示例1:
  1. class Foo:
  2. __x = 1
  3. def test(self):
  4. print(‘from test...‘)
  5. print(Foo.__dict__) # {‘_Foo__x‘: 1}
  6. print(Foo._Foo__x) # 1
实例2:
  1. class People:
  2. """
  3. 变形仅在定义阶段完成,后续不再变形
  4. """
  5. def __init__(self, name, age, sex):
  6. self.__name = name # self._People__name = name
  7. self.__age = age # self._People__age = age
  8. self.__sex = sex # self._People__sex = sex
  9. def tell_info(self):
  10. print(‘人的名字是:%s, 人的性别是: %s, 人的年龄是: %s‘ % (self.__name, self.__sex, self.__age))
  11. p = People(‘rain‘, 18, ‘male‘)
  12. p.tell_info()
  13. 运行结果:
  14. 人的名字是:rain, 人的性别是: male, 人的年龄是: 18
示例3:
  1. class Parent:
  2. def foo(self):
  3. """
  4. # 那么我要让他调用父类中的bar呢
  5. 改成self.__bar()
  6. :return:
  7. """
  8. print(‘from parent foo...‘)
  9. self.bar() # self._Parent__bar
  10. def bar(self): # _Parent__bar
  11. """
  12. 改成def __bar(self):
  13. :return:
  14. """
  15. print(‘from parent bar...‘)
  16. class Sub(Parent):
  17. def bar(self): # _Sub__bar
  18. print(‘from sub bar...‘)
  19. a = Sub()
  20. a.foo() # 默认按照查找顺序,对象本身-->对象所属的类-->父类:from sub bar...
示例4:
  1. class People:
  2. def __init__(self, name, age):
  3. self.__name = name
  4. self.__age = age
  5. def tell_info(self):
  6. print(‘人的名字是:%s, 人的年龄是: %s‘ % (self.__name, self.__age))
  7. def set_info(self, x, y):
  8. """
  9. 我们为了限制输入的类型:
  10. 比如名字规定必须是字符串,年龄必须是整形,
  11. 就需要加一点逻辑判断:isinstance(a,b)
  12. :param x:
  13. :param y:
  14. :return:
  15. """
  16. if not isinstance(x, str):
  17. raise TypeError(‘名字必须是字符串...‘)
  18. if not isinstance(y, int):
  19. raise TypeError(‘年龄必须是整形...‘)
  20. self.__name = x
  21. self.__age = y
  22. p = People(‘alex‘, 1000)
  23. p.tell_info()
  24. p.set_info(‘alex‘, 2000) # 修改
  25. p.tell_info()
  26. p.set_info(1111, 3000) # TypeError: 名字必须是字符串...
  27. p.tell_info()
  28. p.set_info(‘alex‘, ‘3000‘) # TypeError: 年龄必须是整形...
  29. p.tell_info()
 
自动变形的特点:
1,类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果:self._self__x
2,这种变形起始正式针对外部的变形,在外部是无法通过__x这个名字访问到的
3,在子类定义的__x不会覆盖在父类定义的__x,因为子类中在定义的时候自动变形为了:_子类名__x,而父类中变形成了:_父类名__x,即双下划线开头的属性在继承给子类的时候,子类是无法覆盖的.
 
注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(即接口函数),在它内部访问被隐藏的属性,然后外部就可以正常使用了.
 
也可以使用property(特性)来代替接口函数
 
property(特性)
内置函数,装饰器
一旦在函数属性前加上装饰器property,这个函数就会变成一个数据属性
 
基本语法:
  1. class Foo:
  2. @property
  3. def test(self):
  4. print(‘from Foo.test...‘)
    1. # test=property(test)
  5. f = Foo()
  6. #f.test() # 结果:from Foo.test...
  7. f.test # 结果:from Foo.test...
 
实际应用:
示例1:
计算成人BMI值
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖:高于32
体质指数(BMI) = 体重(kg) / (身高(m) ^ 2)
  1. class People:
  2. def __init__(self, name, weight, height):
  3. self.name = name
  4. self.weight = weight
  5. self.height = height
  6. @property
  7. def bmi(self):
  8. return self.weight / (self.height ** 2)
  9. p = People(‘rain‘, 75, 1.80)
  10. # print(p.bmi())
  11. print(p.bmi)
  12. 运行结果:
  13. 23.148148148148145
示例2:
计算圆的周长和面积
  1. import math
  2. class Circle:
  3. def __init__(self, radius): # 圆的半径radius
  4. self.radius = radius
  5. @property
  6. def area(self): # 计算圆的面积
  7. return math.pi * self.radius ** 2
  8. @property
  9. def perimeter(self): # 计算圆的周长
  10. return 2 * math.pi * self.radius
  11. c = Circle(10)
  12. print(c.radius)
  13. print(c.area)
  14. print(c.perimeter)
  15. 运行结果:
  16. 10
  17. 314.1592653589793
  18. 62.83185307179586
[注意:加了装饰器property,arear和perimeter不能被赋值]
 
示例3:
  1. class People:
  2. def __init__(self, name, permission=False):
  3. self.__name = name # 可转换为:self._People__name = name
  4. self.permission = permission # 用于判断,是否允许删除
  5. @property # 可转换为:name = property(name)
  6. def name(self):
  7. return self.__name # 可转换为:self._People__name
  8. @name.setter # name = name.setter(name)
  9. def name(self, value):
  10. if not isinstance(value, str): # 判断value是否是字符串
  11. raise TypeError(‘输入的内容必须是字符串.‘)
  12. self.__name = value # self._People__name = value
  13. @name.deleter
  14. def name(self):
  15. if not self.permission:
  16. raise TypeError(‘禁止删除.‘)
  17. del self.__name
  18. print(‘已删除名字‘)
  19. p = People(‘rain‘)
  20. print(p.name())
  21. print(p.name)
  22. p.name = ‘rain656‘
  23. print(p.name)
  24. p.name = 123123
  25. print(p.name)
  26. p.permission = True
  27. del p.name
  28. print(p.name)
 
绑定方法与非绑定方法:
 
绑定方法:绑定给谁,谁来调用就自动将它本身当作第一个参数传入
-绑定到类的方法:用classmethod装饰器装饰的方法
    对象也可以条用,但仍将类当作第一个参数传入
-绑定到对象的方法:没有被任何装饰器装饰的方法
 
非绑定方法:用staticmethod装饰器装饰的方法
-不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说,就是一个普通工具而已
 
staticmethod示例:
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
  1. import hashlib
  2. import time
  3. class MySQL:
  4. def __init__(self,host,port):
  5. self.id=self.create_id()
  6. self.host=host
  7. self.port=port
  8. @staticmethod
  9. def create_id(): #就是一个普通工具
  10. m=hashlib.md5(str(time.clock()).encode(‘utf-8‘))
  11. return m.hexdigest()
  12. print(MySQL.create_id)
  13. conn=MySQL(‘127.0.0.1‘,3306)
  14. print(conn.create_id)
  15. 运行结果:
  16. <function MySQL.create_id at 0x0000000000D6E378>
  17. <function MySQL.create_id at 0x0000000000D6E378>
 
classmethod示例:
classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法
 
settings.py文件内容:
  1. HOST=‘127.0.0.1‘
  2. PORT=3306
  3. DB_PATH=r‘C:\Users\Administrator\PycharmProjects\test\面向对象编程\test1\db‘
 
  1. import settings
  2. import hashlib
  3. import time
  4. class MySQL:
  5. def __init__(self, host, port):
  6. self.host = host
  7. self.port = port
  8. @classmethod
  9. def from_conf(cls):
  10. print(cls)
  11. return cls(settings.HOST, settings.PORT)
  12. print(MySQL.from_conf)
  13. conn=MySQL.from_conf()
  14. print(conn.host, conn.port)
  15. conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类
  16. 运行结果:
  17. <bound method MySQL.from_conf of <class ‘__main__.MySQL‘>>
  18. <class ‘__main__.MySQL‘>
  19. 127.0.0.1 3306
  20. <class ‘__main__.MySQL‘>
 
比较staticmethod和classmethod的区别
示例1:
  1. import settings
  2. class MySQL:
  3. def __init__(self, host, port):
  4. self.host = host
  5. self.port = port
  6. @staticmethod
  7. def from_conf():
  8. return MySQL(settings.HOST,settings.PORT)
  9. @classmethod
  10. def from_conf(cls):
  11. return cls(settings.HOST,settings.PORT)
  12. def __str__(self):
  13. return ‘就不告诉你...‘
  14. class Mariadb(MySQL):
  15. def __str__(self):
  16. return ‘host:%s, port: %s‘ %(self.host,self.port)
  17. m=Mariadb.from_conf()
  18. print(m)
  19. 运行结果:
  20. # @classmethod存在时运行结果:host:127.0.0.1, port: 3306
  21. # @classmethod不存在时运行结果:就不告诉你...
 
示例2:
定义MySQL类
1.对象有id、host、port三个属性
2.定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一
3.提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化
4.为对象定制方法,save和get,save能自动将对象序列化到文件中,文件名为id号,文件路径为配置文件中DB_PATH;get方法用来从文件中反序列化出对象
  1. import settings
  2. import hashlib
  3. import time
  4. import random
  5. import pickle
  6. import os
  7. class MySQL:
  8. def __init__(self,host,port):
  9. self.id=self.create_id()
  10. self.host=host
  11. self.port=port
  12. def save(self):
  13. file_path=r‘%s%s%s‘ %(settings.DB_PATH,os.sep,self.id)
  14. pickle.dump(self,open(file_path,‘wb‘))
  15. def get(self):
  16. file_path=r‘%s%s%s‘ %(settings.DB_PATH,os.sep,self.id)
  17. return pickle.load(open(file_path,‘rb‘))
  18. @staticmethod
  19. def create_id():
  20. m=hashlib.md5(str(time.clock()).encode(‘utf-8‘))
  21. return m.hexdigest()
  22. @classmethod
  23. def from_conf(cls):
  24. print(cls)
  25. return cls(settings.HOST,settings.PORT)
  26. print(MySQL.from_conf) #<bound method MySQL.from_conf of <class ‘__main__.MySQL‘>>
  27. conn=MySQL.from_conf()
  28. print(conn.id)
  29. print(conn.create_id())
  30. print(MySQL.create_id())
  31. conn.save()
  32. obj=conn.get()
  33. print(obj.id)
  34. 运行结果:
  35. <bound method MySQL.from_conf of <class ‘__main__.MySQL‘>>
  36. <class ‘__main__.MySQL‘>
  37. 36d644ee6ae993da6a1e9530bbd7edfe
  38. 387747d3e0c9034e68cb459b89322364
  39. 578a0ec95b1d051128dcdfba3f673754
  40. 36d644ee6ae993da6a1e9530bbd7edfe
 
示例3:
  1. import time
  2. class Date:
  3. def __init__(self,year,month,day):
  4. self.year=year
  5. self.month=month
  6. self.day=day
  7. @staticmethod
  8. def now():
  9. t=time.localtime()
  10. return Date(t.tm_year,t.tm_mon,t.tm_mday)
  11. @staticmethod
  12. def tomorrow():
  13. t=time.localtime(time.time()+86400)
  14. return Date(t.tm_year,t.tm_mon,t.tm_mday)
  15. a=Date(‘1987‘,11,21)
  16. b=Date.now()
  17. c=Date.tomorrow()
  18. print(a.year,a.month,a.day)
  19. print(b.year,b.month,b.day)
  20. print(c.year,c.month,c.day)
  21. 运行结果:
  22. 1987 11 21
  23. 2017 6 13
  24. 2017 6 14
 
示例4:
  1. import time
  2. class Date:
  3. def __init__(self,year,month,day):
  4. self.year=year
  5. self.month=month
  6. self.day=day
  7. @staticmethod
  8. def now():
  9. t=time.localtime()
  10. return Date(t.tm_year,t.tm_mon,t.tm_mday)
  11. class EuroDate(Date):
  12. def __str__(self):
  13. return ‘year:%s month:%s day:%s‘ %(self.year,self.month,self.day)
  14. e=EuroDate.now()
  15. print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
  16. ‘‘‘
  17. 输出结果:
  18. <__main__.Date object at 0x1013f9d68>
  19. ‘‘‘
  20. #因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod
  21. import time
  22. class Date:
  23. def __init__(self,year,month,day):
  24. self.year=year
  25. self.month=month
  26. self.day=day
  27. # @staticmethod
  28. # def now():
  29. # t=time.localtime()
  30. # return Date(t.tm_year,t.tm_mon,t.tm_mday)
  31. @classmethod #改成类方法
  32. def now(cls):
  33. t=time.localtime()
  34. return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化
  35. class EuroDate(Date):
  36. def __str__(self):
  37. return ‘year:%s month:%s day:%s‘ %(self.year,self.month,self.day)
  38. e=EuroDate.now()
  39. print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
  40. 运行结果:
  41. <__main__.Date object at 0x0000000000AA0908>
  42. year:2017 month:6 day:13
__init__:类实例初始化函数
__str__:类实例字符串化函数
 
 
反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)
python面向对象中的反射:通过字符串的形式操作对象相关的属性;
python中的一切事物都是对象(都可以使用反射)
例如:
  1. class Chinese:
  2. country = ‘China‘
  3. def __init__(self,name,age):
  4. self.name=name
  5. self.age=age
  6. p=Chinese(‘jack‘, 19)
  7. print(Chinese.country) # 可转换为Chinese.__dict__[‘country]
  8. print(p.name) # 可转换为p.__dict__[‘name‘]
转换的部分就是一种本质上的反射
 
下列方法可以实现自省的函数:
hasattr(obj,name):判断obj是否有name属性,name需为字符串类型
 
getattr(obj,name,default=None):获取对象obj的name属性,default表示设置默认返回值
 
setattr(x,y,v):设置对象x的属性y,值为v,y必须为字符串类型
 
delattr(x,y):删除类x的属性y,y需为字符串形式
 
方法示例:
  1. class BlackMedium:
  2. feature=‘Ugly‘
  3. def __init__(self,name,addr):
  4. self.name=name
  5. self.addr=addr
  6. def sell_house(self):
  7. print(‘%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼.‘ % self.name)
  8. def rent_house(self):
  9. print(‘%s 黑中介租房子啦,傻逼才租呢.‘ % self.name)
  10. # 检测是否含有某个属性
  11. b1 = BlackMedium(‘万成‘,‘回龙观‘)
  12. print(hasattr(b1,‘name‘)) # 判断对象b1是否有‘name‘这个属性,判断的属性必须是字符串形式
  13. print(hasattr(b1,‘sell_house‘))
  14. # 获取某个属性
  15. n = getattr(b1,‘name‘)
  16. print(n)
  17. func=getattr(b1,‘rent_house‘)
  18. func()
  19. # getattr(b1,‘aaaaa‘) # 提示报错
  20. print(getattr(b1,‘aaaaa‘,‘不存在...‘)) # 提示‘不存在‘
  21. # 设置某个属性
  22. setattr(b1,‘sb‘,True) # 设置对象b1的属性‘sb‘,值为True
  23. setattr(b1,‘show_name‘,lambda self:self.name+‘sb‘) # 设置b1属性‘show_name‘
  24. print(b1.__dict__) # 查看对象b1的属性字典
  25. print(b1.show_name(b1))
  26. # 删除属性
  27. delattr(b1,‘addr‘)
  28. delattr(b1,‘show_name‘)
  29. # delattr(b1,‘show_name111‘) # 不存在则报错
  30. print(b1.__dict__)
 
类也是对象,同对象一样支持反射的四个方法
  1. class Foo(object):
  2. staticField = "old boy"
  3. def __init__(self):
  4. self.name = ‘wupeiqi‘
  5. def func(self):
  6. return ‘func‘
  7. @staticmethod
  8. def bar():
  9. return ‘bar‘
  10. print getattr(Foo, ‘staticField‘)
  11. print getattr(Foo, ‘func‘)
  12. print getattr(Foo, ‘bar‘)
 
模块也是对象,也适用四个方法
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. import sys
  4. def s1():
  5. print ‘s1‘
  6. def s2():
  7. print ‘s2‘
  8. this_module = sys.modules[__name__]
  9. hasattr(this_module, ‘s1‘)
  10. getattr(this_module, ‘s2‘)
导入其他模块,利用反射查找该模块是否存在某个方法
模块文件:module_test.py
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. def test():
  4. print(‘from the test‘)
导入module.py模块,利用反射查找该模块是否存在
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. """
  4. 程序目录:
  5. module_test.py
  6. index.py
  7. 当前文件:
  8. index.py
  9. """
  10. import module_test as obj
  11. #obj.test()
  12. print(hasattr(obj,‘test‘))
  13. getattr(obj,‘test‘)()
 
为什么用反射之反射的好处
 
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
 
示例1:
1:ftpclient.py文件
  1. class FtpClient:
  2. ‘ftp客户端,但是还么有实现具体的功能‘
  3. def __init__(self,addr):
  4. print(‘正在连接服务器[%s]‘ %addr)
  5. self.addr=addr
2:test.py文件
  1. import ftpclient
  2. f1=ftpclient.FtpClient(‘192.168.1.1‘)
  3. if hasattr(f1,‘get‘):
  4. func_get=getattr(f1,‘get‘)
  5. func_get()
  6.  
  7. print(‘处理其他的逻辑代码‘)
示例2:
  1. class FtpCLient:
  2. def __init__(self,host):
  3. self.host=host
  4. print(‘connecting...‘)
  5. def run(self):
  6. while True:
  7. inp=input(‘>>: ‘).strip()
  8. inp_l=inp.split()
  9. if hasattr(self,inp_l[0]):
  10. func=getattr(self,inp_l[0])
  11. func(inp_l)
  12. def get(self,arg):
  13. print(‘download file‘,arg[1])
  14. f=FtpCLient(‘192.168.1.2‘)
  15. f.run()
 
好处二:动态导入模块(基于反射当前模块成员)
 

18-面向对象之基础