Python面向对象的程序设计--总结

面向对象的程序设计

参考

1 什么是面向对象的程序设计

面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤

优点:极大的降低了程序的复杂度

缺点:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。

应用场景:一旦完成基本很少改变的场景,例如Linux內核、Git、Apache等。

面向对象的程序设计的核心是对象

优点:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层、互联网应用、企业内部软件、游戏等

2 类&对象的简单使用

2.1 什么是类&什么是对象

python中一切皆为对象,且python3统一了类与类型的概念,类型就是类

基于面向对象设计一个款游戏:英雄联盟,每个玩家选一个英雄,每个英雄都有自己的特征和和技能,特征即数据属性,技能即方法属性,特征与技能的结合体就一个对象。

从一组对象中提取相似的部分就是类,类所有对象都具有的特征和技能的结合体

在python中,用变量表示特征,用函数表示技能,因而类是变量与函数的结合体,对象是变量与方法(指向类的函数)的结合体

2.2 初始化类和对象

Example 01:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/16
class Garen:  # 定义一个类
    camp = 'Demacia'
    def attack(self):
        print('attack')
# 如何使用类
# 1.实例化
print(Garen)
obj = Garen()   # 实例化
print(obj)
# 2.引用类的特征(类的变量)和技能(类的函数)
obj.attack()
print(obj.camp)
# 如何使用实例
class Garen:  # 定义一个类
    camp = 'Demacia'
    def __init__(self,nickname):
        self.nick = nickname
    def attack(self,enemy):
        print('%s attack %s'%(self.nick,enemy))
# 实例化会自动触发类内部的__init__函数
# 定义一个一个类,相当于定义了一个名称空间
g1 = Garen('草丛伦')  # Garen.__init__(g1,'草丛伦')
g2 = Garen('猥琐伦')
print(Garen.__dict__) # 查看类的名称空间
print(g1.__dict__) # 查看对象的名称空间
print(g1.nick)
print(g2.nick)
print(g1.attack)    # 实例的绑定方法  绑定方法的核心在于绑定,唯一绑定一个确定的对象
print(Garen.attack) # 类的一个函数
g1.attack('alex')   # 加括号运行 会将自身传值给绑定方法 self = g1
# Garen.attack('obj','somebody')  # 需要一个位置参数self
# 总结
# 类:1.实例化  2.引用名字(类名.变量名 类名.函数名)
# 实例:引用名字(实例名.类的变量  实例名.绑定方法  实例名.实例自己的变量名)
print(Garen.camp)    # 查看
Garen.camp = 'test'  # 修改
print(Garen.camp)
Garen.add_value = 1  # 增加
print(Garen.add_value)
del Garen.add_value  # 删除
g1 = Garen('alex')
print(g1.nick)  # 查看  先从对象本身找该变量,如果没有就去类找这个变量
g1.nick = 'aSB' # 修改
del g1.nick     # 删除
g1.sex = 'female'  # 增加
print(g1.sex)

Example 02:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/22
# 应用场景
# 对象: 学校 --> 归类
# 共有的特征: 商标为etiantian
# 共有的技能: 招生
# 独有的特征: 地址 老师 课程 不一样
class School:
    tag = 'etiantian'
    def zhaosheng(self):
        pass
    def __init__(self,addr):
        self.addr = addr
        self.teacher_list = []
        self.course_list = []
# 对象: 老师 --> 归类
# 共同的技能: 教课
# 独有的特征: 名字 level 性别 课程
class Teacher:
    def __init__(self,name,sex,level):
        self.name = name
        self.sex = sex
        self.level = level
        self.course_list = []
    def teach(self):
        pass
# 对象: 学生 --> 归类
# 共有的技能: search_score  handin
# 共有的特征: 学号 名字 性别 课程
class Student:
    def __init__(self,ID,name,sex):
        self.id = ID
        self.name = name
        self.sex = sex
        self.course_list = []
    def search_score(self):
        pass
    def handin(self):
        pass
# 对象: 课程
class Course:
    def __init__(self,name,price,period):
        self.name = name
        self.price = price
        self.period = period
class Course:
    def __init__(self,name,price,period):
        self.name = name
        self.price = price
        self.period = period
s1 = Student('321789466','cobila','female')
print(s1.id,s1.name,s1.sex)
print(s1.course_list)
# s1.course_list.append('python')
# s1.course_list.append('linux')
# print(s1.course_list)
python = Course('python',15800,5)
linux = Course('linux',12800,5)
s1.course_list.append(python)
s1.course_list.append(linux)
print(s1.course_list)
print('''Student name is : %s
    course name is : %s
    course price is : %s
    ''' % (s1.name,s1.course_list[0].name,s1.course_list[0].price))

3 类的特性

  • 封装
  • 继承
  • 组合
  • 多态

3.1 封装

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

# 封装
# 封装数据的主要原因是: 保护隐私
# 封装方法的主要原因是: 隔离复杂度
class A:
    __x = 1  # 变形成了_A__x = 1
    def test(self):
        self.__fa()  # self._A__fa()
    def tell(self):
        print(self.__x)
    def __fa(self):  # _A__fa
        print('from A')
class B(A):
    def __fa(self): # _B__fa
        print('from B')
b = B()
b.test()
# __名字  这种语法,只在定义的时候才会有变形的效果,如果类或者对象已经产生了,就不会有效果
class B:
    pass
B.__x = 1  # 此处不会变形

3.2 继承

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时

我们不可能从头开始写一个类B,这就用到了类的继承的概念。

通过继承的方式新建类B,让B继承A,B会'遗传'A的所有属性(数据属性和函数属性),实现代码重用

Example 01:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/19
# 1 什么是继承?继承是一种创建新的类的方式
# 2 如何继承 --> 如何寻找继承关系
# 继承是一种'是'的关系
# 人类/猪类/狗类都继承动物类
# 3 为什么要用继承
# 解决代码重用问题
# 解决什么是什么的问题
# 4 派生: 子类继承了父类的属性,然后衍生出自己新的属性
# 如果子类衍生出的新的属性与父类的某个属性名字相同
# 那么再调用子类的这个属性,就以子类自己这里的为准
# 继承
class Hero:
    hero = 'From hero'
    def __init__(self,name,value):
        self.nickname = name
        self.lifevalue = value
    def attack(self,emery):
        print('Hero attack')
    pass
class Garen(Hero):
    camp = 'Demacia'
    def __init__(self,name,value,script):
        Hero.__init__(self,name,value)
        self.script = script
    def attack(self,emery):
        Hero.attack(self,emery)
        print('%s attack %s'%(self.nickname,emery))
class Riven(Hero):
    camp = 'Noxus'
    def __init__(self,name,value,script):
        Hero.__init__(self,name,value)
        self.script = script
    def attack(self, emery):
        Hero.attack(self, emery)
        print('%s attack %s' % (self.nickname, emery))
g1 = Garen('SB',500,'生命不息,战斗不止')
r1 = Garen('RV',500,'我已经觉醒了')
print(g1.script)
g1.attack(r1.nickname)
print(r1.script)
r1.attack(g1.nickname)
# 继承顺序
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中统一都是新式类
# python2中才分新式类与经典类 

3.3 组合

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/19
# 组合
class Teacher:
    def __init__(self,name,sex,course,birthday):
        self.name = name
        self.sex = sex
        self.course = course
        self.birthday = birthday
class Student:
    def __init__(self,name,sex,course,birthday,homework):
        self.name = name
        self.sex = sex
        self.course = course
        self.birthday = birthday
        self.homework = homework
class Course:
    def __init__(self,name,price,period):
        self.name = name
        self.price = price
        self.period = period
class Time:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
class Homework:
    def __init__(self,score,level):
        self.score = score
        self.level = level
course1 = Course('python',15800,7)
bir1 = Time('1990','10','18')
hw1 = Homework(100,'perfect')
stu1 = Student('suhaozhi','female',course1,bir1,hw1)
t1 = Teacher('egon','male',course1,bir1)
print(stu1.homework.score)
print(stu1.homework.level)
print(t1.course.name)
print(t1.course.price)

组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同:

1.继承的方式

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师

2.组合的方式

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

3.4 多态

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/19
# 多态
class Animal:
    def run(self):
        raise AttributeError('子类必须实现这个方法')
class Pig(Animal):
    def run(self):
        print('Pig is running')
class People(Animal):
    def run(self):
        print('Men is running')
peo1 = People()
pig1 = Pig()
peo1.run()
pig1.run()
# 多态: 同一种事物的多种形态,动物分为人类/猪类
# 多态性依赖于 继承
# 多态性定义: 定义统一的入口,可以传入不同类型的值,但是调用的逻辑都一样,执行的结果却不一样
# 优点:
# 1 增加了程序的灵活性
# 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,例如func(animal)
# 2 增加了程序的可扩展性
# 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是func(animal)去调
def func(obj):
    obj.run()
func(peo1)
func(pig1)

4 接口归一化设计

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。

然后让子类去实现接口中的函数。

这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/19
# 接口归一化设计
# 范例1 raise实现接口归一化
class Animal:   # 父类里只定义子类需要有哪些功能,具体实现需要在子类里
    def run(self):
        raise AttributeError('子类必须实现这个方法')
    def speak(self):
        raise AttributeError('子类必须实现这个方法')
class Pig(Animal):
    def run(self):
        print('Pig is walking')
    def speak(self):
        print('Pig is speaking')
class People(Animal):
    def run(self):
        print('Men is running')
    def speak(self):
        print('Men is speaking')
peo1 = People()
pig1 = Pig()
peo1.run()
peo1.speak()
pig1.run()
pig1.speak()
# 范例2
class Interface: # 定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
    def read(self): #定接口函数read
        pass
    def write(self): #定义接口函数write
        pass
class Txt(Interface):  # 文本,具体实现read和write
    def read(self):
        print('文本数据的读取方法')
    def write(self):
        print('文本数据的读取方法')
class Sata(Interface): # 磁盘,具体实现read和write
    def read(self):
        print('硬盘数据的读取方法')
    def write(self):
        print('硬盘数据的读取方法')
class Process(Interface):  # 进程,具体实现read和write
    def read(self):
        print('进程数据的读取方法')
    def write(self):
        print('进程数据的读取方法')
t1 = Txt()
s1 = Sata()
p1 = Process()
t1.read()
t1.write()
s1.read()
s1.write()
p1.read()
p1.write()
# 抽象类实现接口归一化
# 抽象类: 本质还是类,与普通类的区别在于,加了装饰器的函数子类必须实现
import abc
class Animal(metaclass=abc.ABCMeta):   # 指定metaclass的值
    @abc.abstractmethod   # 加装饰器实现
    def run(self):
        pass
    @abc.abstractmethod   # 加装饰器实现
    def speak(self):
        pass
class Pig(Animal):
    def run(self):
        print('Pig is walking')
    def speak(self):
        print('Pig is speaking')
class People(Animal):
    def run(self):
        print('Men is running')
    def speak(self):
        print('Men is speaking')
peo1 = People()
pig1 = Pig()
peo1.run()
peo1.speak()
pig1.run()
pig1.speak()

5 面向对象相关内置函数

  • super
  • property
  • classmethod
  • staticmethod

5.1 super

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/23

# super用法
# In python2
# 1 super(自己的类,self).父类的函数名字
# 2 super只能用于新式类 即class test(object)方式定义的
# In Python3
# super().父类的函数名字
class People:
    def __init__(self,name,sex,age):
        self.name = name
        self.sex = sex
        self.age = age
    def walk(self):
        print('%s is walking'%self.name)
class Chinese(People):
    country = 'China'
    def __init__(self,name,sex,age,language = 'Chinese'):
        super().__init__(name,sex,age)
        self.language = language
    def walk(self):
        super().walk()
        print('------')
class North_korean(People):
    country = 'Korean'
c = Chinese('egon','male',18)
print(c.name,c.sex,c.age)
c.walk()

5.2 property

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/23
import math
# property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
class Circle:
    def __init__(self,radius):
        self.radius = radius
    @property  # area = property(area)
    def area(self):
        return math.pi * self.radius ** 2  # 计算面积
    @property  # perimeter = property(perimeter)
    def perimeter(self):
        return 2 * math.pi * self.radius  # 计算周长
c = Circle(7)
# print(c.area())
# print(c.perimeter())
print(c.area)
print(c.perimeter)
class People():
    def __init__(self,name,age,height,weight):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
    @property
    def bodyindex(self):
        return self.weight / (self.height ** 2)
p1 = People('cobila',38,1.65,74)
print(p1.bodyindex)
# perproty 补充
class People:
    def __init__(self,name):
        self.__Name = name
    @property
    def name(self):
        return self.__Name
    @name.setter  # 被property装饰过的属性,会有一个setter方法
    def name(self,value):
        if not isinstance(value,str):
            raise TypeError('Name must be str')
        self.__Name = value
    @name.deleter
    def name(self):
        del self.__Name
p1 = People('cobila')
# print(p1._People__Name)
print(p1.name)
p1.name = 'change name'
print(p1.name)
# del p1.name
# print(p1.name)

5.3 classmethod

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/23
# classmethod
class Foo:
    @classmethod  # 类的绑定方法,默认会把类当做第一个参数给函数
    def test(cls,x):
        print('classmethod',cls,x)
f1 = Foo()
f1.test(100)  # 类的对象也可以调用,但是第一个参数传的还是类
Foo.test(100)

5.4 staticmethod

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/23
# staticmethod
class Foo:
    def spam(self):
        print('----->')
f1 = Foo()
f1.spam()
class Foo:
    @staticmethod
    def spam(self,x,y,z):
        print(x,y,z)
f2 = Foo()
f2.spam(f2,1,2,3)
# staticmethod 应用
import time
# 是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
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():
        t = time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)
d1 = Date.now()
print(d1.year,d1.month,d1.day)
d1 = Date.tomorrow()
print(d1.year,d1.month,d1.day)

6 基于类和对象反射的属性

反射的精髓是通过字符串获取属性

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/24
# 基于类和对象反射的属性
class People:
    country = 'China'
    def __init__(self,name):
        self.name = name
    def run(self):
        print('%s is running'%self.name)
p = People('egon') # 实例化一个People对象p
print('基于类和对象反射的属性')
# hasattr: 判断类或对象是否存在某个属性,存在返回True,否则返回False
print('hasattr------------>')
print(hasattr(p,'name'))
print(hasattr(People,'name'))
# getattr: 获取类或对象的属性
print('getattr------------->')
print(getattr(p,'run'))   # 相当于print(p.run)
print(getattr(People,'run'))
func = getattr(p,'run')
func()
# hasattr和getattr结合使用
print('hasattr & getattr------------>')
if hasattr(p,'run'):
    func = getattr(p,'run')  # 相当于 func = p.run
    func()
# setattr: 修改或新增类或对象的属性
print('setattr-------------->')
print(p.__dict__)
setattr(p,'name','cobila')  # 用于修改 相当于 p.name = 'cobila'
setattr(p,'age',18)  # 用于新增  相当于 p.age = 18
print(p.__dict__)
# delattr: 删除类或对象的属性
print('delattr------------->')
print(p.__dict__)
delattr(p,'name')  # 相当于 del p.name    
print(p.__dict__)
# 反射的应用
import sys
def add():
    print('add')
def delete():
    print('delete')
def update():
    print('update')
def get():
    print('get')
this_module = sys.modules[__name__]
func_dict = {'add':add,'delete':delete,'update':update,'get':get}
while True:
    choice = input('>>').strip()
    if hasattr(this_module,choice):
        func_dict[choice]

7 attr系列(__setattr__ __delattr__ __getattr__)

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/24
# class __getattr__ __setattr__ __delattr__
class Foo:
    def __init__(self,name,age):
        self.name = name  # 会触发__setattr__
        self.age = age  # 会触发__setattr__
    def __setattr__(self, key, value):
        print('setattr')
        if not isinstance(value,str): # 可以设置类型限制
            raise TypeError("must be str")
        self.__dict__[key] = value
    def __getattr__(self, item):  # 属性不存在时会执行此函数
        print('getattr')
    def __delattr__(self, item):
        print('delattr')
        self.__dict__.pop(item)
f = Foo('egon',18)  # 初始化会触发__setattr__
f.name = 'cobila'   # 会触发__setattr__
print(f.__dict__)
f.xxx            # 找不到xxx属性时会触发__getattr__
del f.age        # 会触发__delattr__
print(f.__dict__)

8 定制自己的数据类型

8.1 通过继承

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/24
# 定制自己的数据类型 通过继承
class List(list):
    def append(self, p_object):
        if not isinstance(p_object,int):
            raise TypeError('Must be int')
        super().append(p_object)
    def insert(self,index,p_object):
        if not isinstance(p_object, int):
            raise TypeError('Must be int')
        super().insert(index,p_object)
l1 = List([1,2,3])
print(l1)
l1.append(4)
print(l1)
# l1.append('test') # 会抛出TypeError异常
l1.insert(0,5)
# l1.insert(0,'5')  # 会抛出TypeError异常
print(l1)

8.2 通过授权

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/24
# 定制自己的open函数 不通过继承 通过授权的方式实现定制自己的数据类型
import time
print(time.strftime('%Y-%m-%d %X')) # 打印当前时间
class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding
        self.f = open(filepath,mode,encoding=encoding)
    def write(self,line):
        t = time.strftime('%Y-%m-%d %X')
        self.f.write('%s %s
'%(line,t))
    def __getattr__(self,item):
        func = getattr(self.f,item)
        return func
f = Open('b.txt','w')
f.write('test write')
f.write('test write')
f.write('test write')
f.close()
f = Open('b.txt','r+')
res = f.read()
print(res)
f.close()

9 item系类(__setitem__ __getitem__ __delitem__)

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/25
# 类的item系列
# 把对象操作属性模拟成字典的格式
class Foo:
    def __init__(self,name):
        self.name=name

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)

10 __str__ __repr__ __format__

改变对象的字符串显示__str__``__repr__

自定制格式化字符串__format__

Example 01:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/25

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'))

Example 02:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/25

date_dic={
    'ymd':'{0.year}:{0.month}:{0.day}',
    'dmy':'{0.day}/{0.month}/{0.year}',
    'mdy':'{0.month}-{0.day}-{0.year}',
}
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

    def __format__(self, format_spec):
        if not format_spec or format_spec not in date_dic:
            format_spec='ymd'
        fmt=date_dic[format_spec]
        return fmt.format(self)

d1=Date(2016,12,29)
print(format(d1))
print('{:mdy}'.format(d1))

11 issubclass & isinstance

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/25

class A:
    pass

class B(A):
    pass

print(issubclass(B,A)) #B是A的子类,返回True

a1=A()
print(isinstance(a1,A)) #a1是A的实例

12 __slots__

Example 01:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/25
'''
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。           更多的是用来作为一个内存优化工具。

'''
class Foo:
    __slots__='x'


f1=Foo()
f1.x=1
f1.y=2#报错
print(f1.__slots__) #f1不再有__dict__

class Bar:
    __slots__=['x','y']
    
n=Bar()
n.x,n.y=1,2
n.z=3  # 报错

Example 02:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Foo:
    __slots__=['name','age']

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
# f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

13 __next__和__iter__实现迭代器协议

Example 01:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Foo:
    def __init__(self,x):
        self.x=x

    def __iter__(self):
        return self

    def __next__(self):
        n=self.x
        self.x+=1
        return self.x

f=Foo(3)
for i in f:
    print(i)

Example 02:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Foo:
    def __init__(self,start,stop):
        self.num=start
        self.stop=stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.num >= self.stop:
            raise StopIteration
        n=self.num
        self.num+=1
        return n

f=Foo(1,5)
from collections import Iterable,Iterator
print(isinstance(f,Iterator))

for i in Foo(1,5):
    print(i)

Example 03:

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13
# 斐波那契数列

class Fib:
    def __init__(self):
        self._a=0
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        self._a,self._b=self._b,self._a + self._b
        return self._a

f1=Fib()

print(f1.__next__())
print(next(f1))
print(next(f1))

for i in f1:
    if i > 100:
        break
    print('%s ' %i,end='')

14 __doc__

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Foo:
    '我是描述信息'
    pass

class Bar(Foo):
    pass
print(Bar.__doc__) # 该属性无法继承给子类

15 __module__ & __class__

__module__表示当前操作的对象在那个模块

__class__表示当前操作的对象的类是什么

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

# test1.py
class C:

    def __init__(self):
        self.name = 'SB'
#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

# test2.py
from test1 import C

obj = C()
print(obj.__module__)     # 输出 test1,即:输出模块
print(obj.__class__)      # 输出 <class 'test1.C'>,即:输出类

16 __del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')
#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
# del f1
print('------->')
'''
输出结果:
------->
执行我啦
'''
# 程序结束时,对象在内存中被释放

17 __enter__ & __exit__

我们知道在操作文件对象的时候可以这么写

with open('a.txt') as f:
    '代码块'

上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter____exit__方法

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:
    print('=====>执行代码块')
    print(f,f.name)

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)



with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True



with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行

用途:

  • 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

  • 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

18 __call__

对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于__call__方法的执行是由对象后加括号触发的,即:对象() 或者 类()

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/13

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__

19 元类

#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/4/26
# 元类 type
# 元类(type) --> 类(class) --> 对象(object)
# 1.通常的创建类的方式
class Foo:  #
    x = 1
    def run(self):
        pass
class test:
    pass
print(Foo.__dict__)
print(Foo.__bases__)
print(type(Foo))
# 2.通过元类(type)创建类的方式
def run():
    pass
class_name = 'Foo'
class_bases = (object,)
class_dict = {'x':1,'run':run}
Foo = type(class_name,class_bases,class_dict)
print(Foo.__dict__)
print(Foo.__bases__)
print(type(Foo))
# 元类应用
# 类的函数必须要写注释(__doc__)
class Mymeta(type):
    def __init__(self,class_name,class_bases,class_dict):
        for key in class_dict:
            if callable(class_dict[key]):
                if not class_dict[key].__doc__:
                    raise TypeError('小子,你没写注释,赶紧去写')
class Foo(metaclass=Mymeta):  # Foo = Mymeta('Foo',(object,),{'x':1,'run':run}) 定义时会执行metaclass的__init__
    x = 1
    def __init__(self,name):
        self.name = name
    def run(self):
        'run func'
        print('running')
print(Foo.__dict__)
# 元类实例化过程
class Mymeta(type):
    def __init__(self,class_name,class_bases,class_dict):
        for key in class_dict:
            pass
    def __call__(self, *args, **kwargs):
        print(self)
        obj = self.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj
class Foo(metaclass=Mymeta):  # Foo = Mymeta('Foo',(object,),{'x':1,'__init__':__init__,'run':run})  调用Mymeta的__init__ 
    x = 1
    def __init__(self,name):
        self.name = name
    def run(self):
        'run func'
        print('running')
f = Foo('egon')  # 调用Mymeta的__call__ 
原文地址:https://www.cnblogs.com/wanyuetian/p/6850795.html