Python第十周学习总结

面向对象,类的继承,多继承,特殊属性,魔术方法,二分法排序等。

一、面向对象

1、语言的分类

 1)面向机器

抽象成机器指令,机器容易理解。代表:汇编语言。

2)面向过程

做一件事,排除步骤,第一步做什么,第二步做什么,如果出现A问题,做什么处理,出现b问题,做什么处理。问题规模小,步骤化,按部就班处理。  代表:c语言。

3)面向对象oop

随着计算机需要解决的问题规模扩大,情况越来越复杂,需要很多人,很多部门协作,面向过程编程太不适合了。代表:c++  java  python 等

2、面向对象

1)定义   (是一种认识世界,分析世界的方法论,用类来表现实实在在的对象。)

一种认识世界、分析世界的方法论,将万事万物抽象为类。

2)类class

类是抽象的概念,是万事万物的抽象,是一类实物的共同特征的集合。

用计算机语言来描述类,就是属性和方法的集合。

3)对象instance、object

对象是类的巨象,是一个实体。

对于每个人这个个体,都是抽象概念人类的不同的实体。

(你吃鱼,你就是对象。鱼,也是对象;吃是动作。你是具体的人,是具体的动作。你属于人类,人类是个抽象的概念,是无数具体的个体的抽象。鱼也是具体的对象,就是说你吃的是一条具体的鱼,这条鱼属于鱼类,是无数的鱼抽象出来的概念。)

(吃,是具体的动作,也是操作,也是方法,这个吃是你的动作,也就是人类具有的方法,如果说的是鱼吃人,吃就是鱼类的动作了)

(吃是个动作,许多动物都具有的动作,人类和鱼类都属于动物类,而动物类是抽象的概念,是动物都有吃的动作,但是吃法不同而已)

(驾驶车,这个车也是车类的具体的对象(实例),驾驶这个动作是鱼类不具有的,是人类具有的方法。)

4)属性

是对象状态的抽象,用数据结构来描述。

5)操作

他是对象行为的抽象,用操作名和实现该操作方法的描述。

(每个人都有名字,身高,体重等信息,这些信息都是个人的属性,但是,这些信息不能保存在人类中,因为他是抽象的概念,不能保留具体的值。)

(而人类的实例,就是具体的人,他可以存储这些具体的特性,而且可以不同的人有不同的属性)

6)哲学

一切皆对象

对象是数据和操作的封装

对象是独立的,但是对象之间可以相互作用。

目前oop是最接近人类认知的编程范式。

3、面向对象3要素****

1)封装

*组装:将数据和操作组装到一起。

*对外只是暴露一些接口,通过接口访问对象。(可供操作的属性,暴漏的属性)

2)继承

*多复用,继承来的就不用自己写了。(用到父类已经做好的实现和操作,在自己内部无需实现了,相同的就不写了,写自己独有的特点。)

*多继承少修改,ocp(open-closed princile),使用继承来改变,来体现个性。(父类的基础上少修改)。

3)多态

*面向对象编程最灵活的地方,动态绑定。

人类就是封装:

人类继承自动物类,孩子继承父母的特征,分为单一继承、多继承;

多态,继承自动物类的人类,猫类的操作吃的不同。

 

(类对象)类的定义

(类的对象)类的实例。

4、Python的类

1)定义

Class  ClassName:

语句块                    (放属性和方法,实例的属性。实例化过程。)

(1)必须使用class关键字。

(2)类名必须是用大驼峰命名。

(3)类定义完成后,就产生了一个类对象,绑定了标示符ClassName上。

(4)举例

class MyClass:
“””A example class”””
    x = 1  #  类属性

def foo(self):   #类属性foo,也是方法
return ” My Class”

print(MyClass.x)
print(MyClass.foo)
print(MyClass.foo(1))
print(MyClass.__doc__)

 

No1 print:   1

No2 print:   <function MyClass.foo at 0x0000002E421852F0>  (说是个函数类在某个地址里面)

No3 print:  My Class

No4 print:  A example class

 

 

(标示符就是一个属性)

5、类对象及类属性

1)类对象

类的定义就会产生一个类对象。

2)类属性

类定义中的变量和类中定义的方法都是类的属性。

3)类变量

上类中的x是类MyClass的变量。

(加括号调用方法,不加是调用的属性。)

4)总结

MyClass中  x、foo都是类的属性。

foo方法都是类的属性,如同吃是人类的方法。但是每个具体的人才能吃东西,也就说吃人的实例才能调用的方法。

 

foo 是方法对象method,不是普通的函数对象function了,他一般要求至少有一个参数。第一个参数可以是self(self是标示符),这个参数位置就留给了self

6、类的实例

调用MyClass().Show()     (new函数和init函数都调用了)  带有self的都是方法。

调用 a = MyClass()

 

背后是要进行绑定的。

前后都有下划线的称为魔术方法。初始化方法。

 

7、实例化

a = MyClass()

1)定义方法:

使用上面的语法,在类对象名称后面加上一个括号,就是调用类的实例化方法,完成实例化。实例化就是真正创建一个该类的对象(实例)。

tom = MyClass()

Jerry = MyClass()

tom /jerry都是MyClass的实例,通过实例化生成了2个实例。

 

每次实例化后获得实例,是不同的实例,即使是使用同样的参数实例化,也得到不同的对象。

Python类实例化后,会自动调用__init__,这个方法第一个参数都必须留给self,其它参数随意。

2)__init__

3)MyClass()实际上调用的是__init__(self)方法,可以不定义,如果没有定义会在实例化后隐式调用。

作用:对实例进行初始化(self当前对象的本身)。实例带初始化。

 

初始化函数可以多个参数,第一个参数位置必须是self。

__init__()方法不能有返回值,也就是只能是None。

class MyClass:
“””A example class”””
    x = 1  #  类属性

def foo(self):   #类属性foo,也是方法
return ” My Class”

# print(MyClass)    #不会被调用
print(MyClass())    #会调用
a = MyClass()     #  会被调用。。结果和2一样。
print(a)

 

No1  print:不会被调用

No2 print :<__main__.MyClass object at 0x0000001101903908>

No2 print :<__main__.MyClass object at 0x0000001101903908>

 

class Person:
def __init__(self,name,age):
self.name = name
self.age = age

def showage(self):
print(‘{} is {}’.format(self.name,self.age))

tom = Person(‘Tom’,20)
cat = Person(‘cat’,12)
tom.showage()                   #  Tom is 20
cat.showage()                   #   cat is 12
print(tom.name,cat.name)        #    Tom cat
cat.age += 1
print(cat.age)                   #   13
cat.showage()                   #   cat is 13

 

 

 

每次实例化后都会生成不同的对象。(id地址不同)

 

 

8、实例对象instance

1)类实例化后一定会获得一个对象,就是实例对象。

tom  cat 就是Person类的实例。

__init__ 方法的第一参数self就是指代某一实例。

类实例化后,得到一个实例对象,实例对象都会绑定方法,调用时采用tom.showage()的方式。函数签名是showage(self)。这个self就是tom,python中会把方法的调用者作为第一参数self作为实参传入。

Self,name就是tom对象的name,name是保存在tom对象上面,而不是Person的类上面,所有称为实例变量。

2)Self 实例化。

class MyClass:
def __init__(self):
print(‘self in init = {}’.format(id(self)))

c = MyClass()
print(‘c = {}’.format(id(c)))

 

self in init = 583263140104

c = 583263140104

Self就是调用者,就是c对应的实例对象。

Self这个名字只是一个惯例,可以修改,最好不要修改,影响可读性。

 

 

 

 

 

9、实例变量和类变量

 

class Person:
age = 3
def __init__(self,name):
self.name = name

tom = Person(‘tom’)
cat = Person(‘cat’)

print(tom.name,tom.age)
print(cat.name,cat.age)
print(Person.age)
Person.age = 30
print(Person.age,tom.age,cat.age)

 

tom 3

cat 3

3

30 30 30

 

 

实例变量是每一个实例自己的变量,是自己独有的。类变量是累的变量,是类的所有实例共享和使用的方法。

特殊属性 含义
__name__ 对象名
__class__ 对象的模型
__dict__ 对象的属性的字典
__qualname__ 类的限定名

Python中每一种对象都拥有不同的属性,函数、类、都是对象,类的实例也是对象。

 

class Person:
age = 3
def __init__(self,name):
self.name = name

print(‘class———–‘)
print(Person.__class__)
print(sorted(Person.__dict__.items()),end=’\n\n’)

tom = Person(‘tom’)
print(‘—-instance tom —-‘)
print(tom.__class__)
print(sorted(tom.__dict__.items()),end=’\n\n’)

print(‘—–tom class——‘)
print(tom.__class__.__name__)
print(sorted(tom.__class__.__dict__.items()),end= ‘\n\n’)

 

 

class———–

<class ‘type’>

[(‘__dict__’, <attribute ‘__dict__’ of ‘Person’ objects>), (‘__doc__’, None), (‘__init__’, <function Person.__init__ at 0x000000FD0DFF52F0>), (‘__module__’, ‘__main__’), (‘__weakref__’, <attribute ‘__weakref__’ of ‘Person’ objects>), (‘age’, 3)]

 

—-instance tom —-

<class ‘__main__.Person’>

[(‘name’, ‘tom’)]

 

—–tom class——

Person

[(‘__dict__’, <attribute ‘__dict__’ of ‘Person’ objects>), (‘__doc__’, None), (‘__init__’, <function Person.__init__ at 0x000000FD0DFF52F0>), (‘__module__’, ‘__main__’), (‘__weakref__’, <attribute ‘__weakref__’ of ‘Person’ objects>), (‘age’, 3)]

 

 

 

class Person:
age = 3
hight = 170

def __init__(self,name,age=18):
self.name = name
self.age = age

tom = Person(‘tom’)
cat = Person(‘cat’,20)

Person.age = 30
print(Person.age,tom.age,cat.age)

print(Person.hight,tom.hight,cat.hight)
cat.hight = 175
print(Person.hight,tom.hight,cat.hight)
tom.hight +=10
print(Person.hight,tom.hight,cat.hight)

Person.hight +=15
print(Person.hight,tom.hight,cat.hight)

Person.weight = 70
print(Person.weight,tom.weight,cat.weight)

print(tom.__dict__[‘hight’])
# print(tom.__dict__[‘weight’])  不可执行,因为后来定义的字典内没有

 

 

30 18 20

170 170 170

170 170 175

170 180 175

185 180 175

70 70 70

180

 

总结:

是类的,也就是这个类的所有实例,其实例都可以访问的到,是实例的,就是这个实例自己,通过类访问不到。

类变量是属于类的变量,这个类的所有实例可以共享这个变量。

实例可以动态的给自己增加一个属性。实例.__dict__,如果没有,然后通过属性__class__找到自己的类,再去类的__dict__中找。

注意,如果实例使用__dict__[变量名]访问变量,将不会按照上面的顺序查找变量了,这是指明使用字典的key查找,不是属性来查找。

一般来说,类变量使用全部大写来命名。

 

特殊属性

 

 

实例的字典里面放的是和self有关的信息,类字典里面放的是类的信息,类的字典的items。

属性访问的:的找自己的找不到自己的找类的。

字典访问的:指定的是自己的字典。

赋值即定义,定义属性。

 

 

 

 

 

Person().normal()实例调用。必须传参数。不用。

Person.normal()类调用。

10、装饰一个类

 

def add_name(cls):
cls.NAME = name
return cls

@add_name(‘tom’)
class Person:
AGE = 3

 

 

def add_name(name):
def wrapper(cls):
cls.NAME = name
return cls
return wrapper

@add_name(‘tom’)
class Person:
AGE = 3

 

11、类方法和静态方法。

__init__等方法,这些方法的本身都是类的属性,第一个参数必须是self,而self必须指向一个对象,也就是类必须实例化以后,由实例来调用这个方法。

1)普通函数:

class Person:
def normal_method():
print(‘normal’)
# Person.normal_method()   #可以调用
# Person().normal_method()   #不可以调用

print(Person.__dict__)

 

Person.normal_method()

可以被调用,因为这个方法只是被Person这个名词空间管理的一个普通方法,normal只是一个属性而已。

由于normal_method在定义的时候没有指定self。所以不能完成实例对象的banding,不能用,Person().Normal_method()绑定。  虽然语法对的,但是禁止这么写。

2)类方法

class Person:
@classmethod
def class_method(cls):
print(‘class={0.__name__}({0})’.format(cls))
cls.HIGHT = 170

Person.class_method()
print(Person.__dict__)

 

 

class=Person(<class ‘__main__.Person’>)

{‘__module__’: ‘__main__’, ‘class_method’: <classmethod object at 0x000000C59D4A3908>, ‘__dict__’: <attribute ‘__dict__’ of ‘Person’ objects>, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Person’ objects>, ‘__doc__’: None, ‘HIGHT’: 170}

 

 

(1)在类定义中,使用@class装饰器修饰的方法,

(2)必须至少有一个参数,且第一个参数留给了cls,cls指代调用者即类对象自身。

(3)Cls这个标示符可以是任意合法名称,但是为了易读,不建议修改。

(4)通过cls可以直接操作类的属性。

 

3)静态方法。

class Person:
@classmethod
def class_method(cls):
print(‘class={0.__name__}({0})’.format(cls))
cls.HIGHT = 170

@staticmethod
def static_method():
print(Person.HIGHT)

Person.class_method()
Person.static_method()
print(Person.__dict__)

 

 

class=Person(<class ‘__main__.Person’>)

170

{‘__module__’: ‘__main__’, ‘class_method’: <classmethod object at 0x000000E7C4993908>, ‘static_method’: <staticmethod object at 0x000000E7C49938D0>, ‘__dict__’: <attribute ‘__dict__’ of ‘Person’ objects>, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Person’ objects>, ‘__doc__’: None, ‘HIGHT’: 170}

 

 

(1)staticmethod 静态方法

(2)不需要传参。

(3)在类定义中,使用@staticmethod装饰器修饰的方法。

(4)调用时不会隐身传入参数,静态方法,只是表明这个方法属于这个名词空间,函数归在一起,方便管理。

12、方法的调用

class Person:
def normal_method():
print(‘normal’)

def method(self):
print(‘{} method’.format(self))

@classmethod
def class_method(cls):
print(‘class={0.__name__}({0})’.format(cls))
cls.HIGHT = 170

@staticmethod
def static_method():
print(Person.HIGHT)

print(‘—类访问—‘)
print(1,Person.normal_method())    #  可以调用,返回None。
# print(2,Person.method())    #不可以被调用,必须传入参数,一个self
print(3,Person.class_method())   ##返回None
print(4,Person.static_method())   #返回None
print(Person.__dict__)
print(‘实例访问’)
print(‘tom’)
tom = Person()
# print(1,tom.normal_method())   #不能调用,报错 需要参数
print(2,tom.method())      #可以
print(3,tom.class_method())  #可以
print(4,tom.static_method())   #可以

 

总结:

类几乎可以调用所有内部定义的方法,但是调用普通方法的时候会报错,原因是第一参数必须是类的实例。

实例几乎可以调用所有,普通的函数的调用一般不可能出现,因为不允许定义。

 

类除了普通方法都可以调用,普通方法需要对象的实例作为第一参数。

实例可以调用所有类中定义的方法,(静态,类方法),普通方法传入实例本身,静态方法和类方法需要找到实例的类。

 

 

 

 

 

 

 

13、访问控制

1)私有属性(private)

class Person:
def __init__(self,name,age=18):
self.name = name
self.age = age

def growup(self,i=1):   #控制逻辑
if i>0 and i <150:
self.age +=1
p1 = Person(‘tom’)
p1.growup(20)
p1.age =160
print(p1.age)

 

想通过方法来控制属性,但是由于属性在外部可以访问,就直接绕过方法,直接修改属性。私有属性则解决了这个问题。

 

私有属性:

使用双下划线开头的属性名。就是私有属性。

class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age

def growup(self,i=1):   #控制逻辑
if i>0 and i <150:
self.__age +=1
p1 = Person(‘tom’)
p1.growup(20)
# p1.age =160
print(p1.__age)

 

print(p1.__age)

AttributeError: ‘Person’ object has no attribute ‘__age’

访问不到 __age ..

 

class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age

def growup(self,i=1):   #控制逻辑
if i>0 and i <150:
self.__age += 1

def getage(self):
return self.__age
p1 = Person(‘tom’)
print(p1.getage())

Print 19

2)私有属性的本质,

外部访问不到,能够动态增加一个__age?

class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age

def growup(self,i=1):   #控制逻辑
if i>0 and i <150:
self.__age += 1

def getage(self):
return self.__age
p1 = Person(‘tom’)
p1.__age =28
print(p1.__age)
# print(p1.getage())

 

年龄没有被覆盖,全部存在__dict__的字典里面了。

 

私有变量本质:

类定义的是,如果生命一个实例变量的时候,使用双下划线,python的解释器就会将其改名,转换为_类名__变量名 的名称了,所以用原来的名字访问不到了。

class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age

def growup(self,i=1):   #控制逻辑
if i>0 and i <150:
self.__age += 1

def getage(self):
return self.__age
p1 = Person(‘tom’)
p1.__age =28
print(p1.__age)
# print(p1.getage())

p1._Person__age =15
print(p1.getage())
print(p1.__dict__)

 

 

28

15

{‘name’: ‘tom’, ‘_Person__age’: 15, ‘__age’: 28}

##

3)保护变量

在变量名前面使用一个下划线,称为保护变量。

class Person:
def __init__(self,name,age = 18):
self.name = name
self._age = age

tom = Person(‘tom’)
print(tom._age)
print(tom.__dict__)

 

18

{‘name’: ‘tom’, ‘_age’: 18}

_age属性没有改变名称,和普通属性一样,解释器不做任何处理,只是开发者共同的约定,看见这样的变量,就如同私有变量,不直接使用。

4)私有方法

参展保护变量,私有变量,使用单下划线,双下划线命名方法。

class Person:
def __init__(self,name,age =18):
self.name = name
self._age = age

def _getname(self):
return self.name

def __getage(self):
return self._age

tom = Person(‘tom’)
print(tom._getname())    ##没改名
# print(tom.__getage())    ##m没有次属性
print(tom.__dict__)
print(tom.__class__.__dict__)
print(tom._Person__getage())   #改名了

 

tom

{‘name’: ‘tom’, ‘_age’: 18}

{‘__module__’: ‘__main__’, ‘__init__’: <function Person.__init__ at 0x000000F1ECDB52F0>, ‘_getname’: <function Person._getname at 0x000000F1ECDB5620>, ‘_Person__getage’: <function Person.__getage at 0x000000F1ECDB56A8>, ‘__dict__’: <attribute ‘__dict__’ of ‘Person’ objects>, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Person’ objects>, ‘__doc__’: None}

18

5)私有方法的本质

当下划线的方法只是开发者之间的约定,解释器不做任何改变

双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量相同,_类名__昂发名。

方法变量都在类的 __dict__ 里面可以找到。

6)私有成员总结;

在python中使用_单下划线或者_双下划线来标示一个成员被保护或者被私有化隐藏起来,但是不管使用什么样的访问控制,都不能真正的组织用户修改类的成员,python中没有决定的安全环保成员或者私有成员。

因此,前道的下划线只是一种警告或者提醒,要遵守这个约定,除非有必要,否则不能修好或者使用保护成员或者私有成员,更不能修改。

 

 

 

14、属性装饰器

一般很好的设计是:把实例的属性保护起来,不让外部直接访问,外部适应getter读取属性和setter方法设置属性。

class Person:
def __init__(self,name,age=18):
self.name = name
self.__age =age

@property
def age(self):
return self.__age
@age.setter
def age(self,age):
self.__age = age

tom = Person(‘tom’)
print(tom.age)
tom.age = 20
print(tom.age)

 

 

使用property装饰器的时候,三个方法必须同名。

Property装饰器:

后面跟的函数名就是以后的属性名,他就是getter,这个必须有,有了它至少有了只读属性。

 

Setter装饰器

与属性名同名,且接受两个参数,第一个是self,第二个是将要赋值的值,有了它,属性可写。

 

Deleter装饰器

可以控制是否删除属性,很少用。

 

Property装饰器必须在前,setter  和  delter装饰器在后面。

Property装饰器能通过简单的方式,吧对方法的操作变成对属性的访问,并起到了一定的隐藏效果。

 

其它写法:

class Person:
def __init__(self,name,age =20):
self.name = name
self.__age = age

def getage(self):
return self.__age

def setage(self,age):
self.__age =age

def delage(self):
del  self.__age
print(‘del’)

age = property(getage,setage,delage,’age property’)

tom = Person(‘tom’)
print(tom.age)
tom.age = 25
del tom.age

 

 

class Person:
def __init__(self,name,age=20):
self.name =name
self.__age = age

age = property(lambda self:self.__age)

tom = Person(‘tom’)
print(tom.age)

 

 

可读、可写。

15、补丁。

可以通过修改或者替换类的成员。使用着调用的方式没有改变,但是,类提供的功能可以已经改变。

 

猴子补丁:

在运行时,对属性,方法,函数等进行动态替换。

其目的是为了替换,修改来增强,扩展原有的代码的能力。

慎重使用。

 

 

上例中,假设Person类和get_score方法是从数据库中拿数据,但是测试的时候不方便。

使用猴子补丁的方法,替换了get_score方法,返回模拟的数据。

 

16、对象的销毁。

__del__ 方法,称为析构函数(方法)

 

作用:销毁类的时候调用,以释放占用的资源,其中就放些清理资源的代码。比如释放链接。

这个方法不能让对象真正的小虎,只是对象销毁的时候会自动调用它。

使用del的语句删除实例,引用计数减1,当引用计数为0时候,会idong调用__del__方法。

import time

class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age

def __del__(self):
print(‘delete{}’.format(self.name))

def test():
tom = Person(‘tom’)
tom.__del__()
tom.__del__()
tom.__del__()
print(‘=======’)
tom1 = tom
tom3 = tom1
print(1,’del’)
del tom
time.sleep(3)

print(2,’del’)
del tom3
time.sleep(3)
print(‘—–‘)

del tom3
time.sleep(3)
print(‘……..’)
test()

 

 

由于垃圾回收对象销毁时候,才会真正清理对象,还会再之间自动调用__del__ 方法。除非知道自己的目的,否则不会手动清理;不会手动调用这个方法。

17、方法重载。

Python没有重载,不需要重载

Python中,方法(函数)定义中,形参非常灵活,不需要指定类型(就算指定了也只是说明一个说明而非约束,),形参个数也不固定(可变参数),一个函数的定义可以实现很多种不同形式实参的调用,所哟python不需要方法重载。

或者说python本身就是实现了其他语言的重载。

18、封装:

面向对象的三要素之一,封装。

封装:

将数据和操作组织到类中,即属性和方法。

将数据隐藏起来,给使用者提供操作(方法),使用者通过操作就可以获取或者修改数据。

Getter和setter。

通过控制访问,暴漏给适当的数据和操作给用户,该隐藏的隐藏起来,例如保护成员或者私有成员。

19、习题:

1)随机整数生成类

##初步代码:

import random

class Ran:

def __init__(self,start=0,end=100,length=10):
self.start = start
self.end  = end
self.length = length

def array(self):
lst=  [random.randint(self.start,self.end)for i in range(self.length)]
print(lst)

j = Ran()
j.length=5
j.array()

 

 

###第二步改进控制打印长度的

import random

class Ran:

def __init__(self,start=0,end=100,length=10):
self.start = start
self.end  = end
self.length = length

def array(self,length=0):
length = self.length if length<=0 else length
lst=  [random.randint(self.start,self.end)for i in range(length)]
print(lst)

j = Ran()
j.array(20)

####三,利用工具实现的方式

import random
class Ran:
@classmethod
def array(cls,start = 1,end =100,length =10):
return [random.randint(start,end)for _ in range(length)]

a = Ran()
print(a.array())

###利用生成器

import random

class Ran:

def __init__(self,start=0,end=100,length=10):
self.start = start
self.end  = end
self.length = length
self._gen = self._array()

def _array(self):
while True:
yield random.randint(self.start,self.end)

def array(self,length=0):
if length <= 0:
return [next(self._gen)for _ in range(self.length)]
else:
return [next(self._gen)for i in range(length)]

j = Ran()
print(j.array(20))
print(j.array())

import random

class Ran:

def __init__(self,start=0,end=100,length=10):
self.start = start
self.end  = end
self.length = length
self._gen = self._array()

def _array(self):
while True:
yield [random.randint(self.start,self.end)for _ in range(self.length)]

def array(self,length=0):
if length > 0:
self.length = length
return next(self._gen)

j = Ran()
print(j.array(20))
print(j.array())

 

2)利用上次的随机数生成20次,两两组和,打印坐标。

import random

class Ran:

def __init__(self,start=0,end=100,length=10):
self.start = start
self.end  = end
self.length = length

def array(self,length):
length = self.length if length <=0 else length
lst=  [random.randint(self.start,self.end)for i in range(length)]
return lst

j = Ran()
j.array()

class Point:
def __init__(self,x,y):
self.x = x
self.y = y

# points = zip(j.array(),j.array())
# # for x in points:
# #     print(x)

points = [Point(x,y)for x,y in zip(j.array(),j.array())]
print(Point)

3)车辆信息

class Car:

def __init__(self, mark, color, price, speed, **kwargs):

# self.id = genid
self.mark = mark
self.color = color
self.price = price
self.speed = speed
self.__dict__.update(kwargs)

class CarInfo:
cars = []
def addcar(self,car:Car):
self.cars.append(car)

def getall(self):
return self.cars

c1 = CarInfo()
car = Car(‘audi’,’red’,100,100)
c1.addcar(car)
print(c1.getall())

4)实现温度的处理。

class Temp:
    def __init__(self,t,unit = 'c'):
        self._c = None
        self._f = None
        self._k = None

        if unit == 'k':
            self._k = t
            self._c = self.k2c(t)

        elif unit == 'f':
            self._f = t
            self._c = self.f2c(t)

        else:
            self._c = t

    @property
    def c(self):
        return self._c

    @property
    def k(self):
        if self._k is None:
            self._k = self.c2k(self._c)
        return self._k

    @property
    def f(self):
        if self._f is None:
            self._f = self.c2f(self._c)

    @classmethod
    def c2f(cls,c):
        return 9*c/5 + 32

    @classmethod
    def f2c(cls,f):
        return 5*(f-32)/9

    @classmethod
    def k2c(cls,k):
        return k - 273.15

    @classmethod
    def c2k(cls,c):
        return c + 273.15

    @classmethod
    def f2k(cls,f):
        return cls.c2k(cls.f2c(f))

    @classmethod
    def k2f(cls,k):
        return cls.c2f(cls.k2c(k))

5)模拟购物车购物

class Item:
def __init__(self,**kwargs):
self.__spec = kwargs

def __repr__(self):
return str(sorted(self.__spec.items()))

class Cart:
def __init__(self):
self.items = []

def additem(self,item:Item):
self.items.append(item)

def getallitems(self):
return self.items

mycart = Cart()
myphone = Item(mark=’apple’,menory=’4G’)
mycart.additem(myphone)
print(mycart.getallitems())

 

二、类的继承

1、概念;

1)面向对象的三要素之一,继承inheritance

 

人类和猫类都是继承动物类。

个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性。

在面向对象的世界中,从父类中继承,就可以直接拥有父类的属性和方法,这个可以减少代码,增加多复用,子类可以定义自己的属性和方法。

 

class Animal:
def shout(self):
print(‘Animal shouts’)

class Cat:
def shout(self):
print(‘Cat shouts’)

class Dog:
def shout(self):
print(‘Dog shouts’)

a = Animal()
a.shout()

c = Cat()
c.shout()

d = Dog()
d.shout()

 

Animal shouts

Cat shouts

Dog shouts

 

上面的例子的类虽然有关系,但是定义时候并没有建立这种关系,而是各自完成定义。。

 

 

class Animal:
def __init__(self,name):
self._name = name

def shout(self):
print(‘{} shouts’.format(self.__class__.__name__))

@property
def name(self):
return self._name

a = Animal(‘monster’)
a.shout()

class Cat(Animal):
pass

cat = Cat(‘garfield’)
cat.shout()

class Dog(Animal):
pass

dog = Dog(‘ahuang’)
dog.shout()
print(dog.name)

 

 

上例可以看出,通过继承,猫类,狗类,直接继承了父类的属性和方法。

2)继承

Class cat(Animal)这种形式就是从父类继承,括号中写继承的类的列表。

继承可以让子类从父类获取特征(属性和方法)

 

父类Animal 就是cat的父类,也称为基类,超类。

 

子类

Cat 就是Animal的子类,也成为派生类。

2、定义

格式:

Class 子类名(基类1,[基类2,………..]):

语句块

如果类定义的时候,没有基类列表,等同于继承自object,在python3种,object类是所有对象的根基类。

Class  A:

Pass

#等价于

Class  A(object):

Pass

Python支持多继承,继承也可以多级。

 

查看继承的特殊属性和方法:

特殊属性和方法 含义 举例
__base__ 类的基类
__bases__ 类的基类元组
__mro__ 显示方法查找顺序,基类的元组
mro()方法 同上 Int.mro()
__subclasses__() 类的子类列表 Int.__subclasses__()

3、继承中的访问控制

class Animal:
__COUNT = 100
HEIGHT = 0

def __init__(self,age,weight,height):
self.__COUNT += 1
self.age = age
self.__weight = weight
self.HEIGHT = height

def eat(self):
print(‘{}eat’.format(self.__class__.__name__))

def __getweight(self):
print(self.__weight)

@classmethod
def showcount1(cls):
print(cls.__COUNT)

@classmethod
def __showcount2(cls):
print(cls.__COUNT)

def showcount3(self):
print(self.__COUNT)

class Cat(Animal):
NAME = ‘CAT’
__COUNT = 200

# c = Cat()     函数参数错误,传参错误。
c = Cat(3,5,15)
c.eat()    ##cat eat
print(c.HEIGHT)   ##15
# print(c.__COUNT) ##私有不可访问
# c.__getweight()   #私有不可访问
c.showcount1()    #100因为__是改名的,在实例的类找不到,所以利用继承的。
# c.__showcount2()  #不可以,私有的不能访问
c.showcount3()   #101
print(c.NAME)   # CAT

print(‘{}’.format(Animal.__dict__))
print(‘{}’.format(Cat.__dict__))
print(c.__dict__)
print(c.__class__.mro())

 

后四个print

 

1/{‘_Animal__getweight’: <function Animal.__getweight at 0x000000D34976B0D0>, ‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘HEIGHT’: 0, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘showcount3’: <function Animal.showcount3 at 0x000000D34976B268>, ‘showcount1’: <classmethod object at 0x000000D349769748>, ‘eat’: <function Animal.eat at 0x000000D349764BF8>, ‘_Animal__COUNT’: 100, ‘__module__’: ‘__main__’, ‘__init__’: <function Animal.__init__ at 0x000000D349764D08>, ‘_Animal__showcount2’: <classmethod object at 0x000000D349769780>, ‘__doc__’: None}

2/{‘NAME’: ‘CAT’, ‘__module__’: ‘__main__’, ‘_Cat__COUNT’: 200, ‘__doc__’: None}

3/{‘_Animal__COUNT’: 101, ‘HEIGHT’: 15, ‘_Animal__weight’: 5, ‘age’: 3}

4/[<class ‘__main__.Cat’>, <class ‘__main__.Animal’>, <class ‘object’>]

 

从父类继承,自己没有的,就到父类中去找。

私有的都是不可以访问的,但是本质上依然是改了名字放在这个属性的类的__dict__ 中,知道这个新名称就可以直接找到这个隐藏的变量。

 

(类外部是不会被访问的,私有属性,内部可以。)

继承顺序:实例,自己的类,父类。继承类;

 

总结:

继承时候,公有的,子类和实例都可以随意访问,私有成员被隐藏,子类和实例不可直接访问,当私有变量所在的类内的方法都可以访问这个私有变量。

 

属性查找顺序:

实例的__dict__>>    类__dict__如果有继承==〉父类__dict__.

dir()来查看继承属性。

4、方法的重写、覆盖override

class Animal:
def shout(self):
print(‘Animal shouts’)

class Cat(Animal):
#覆盖了父类的方法
def shout(self):
print(‘miao’)
a = Animal()
a.shout()
c =Cat()
c.shout()

print(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)

 

 

{}

{}

{‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘__module__’: ‘__main__’, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘__doc__’: None, ‘shout’: <function Animal.shout at 0x000000E832DF4D08>}

{‘__module__’: ‘__main__’, ‘__doc__’: None, ‘shout’: <function Cat.shout at 0x000000E832DF4BF8>}

 

 

class Animal:
def shout(self):
print(‘Animal shouts’)

class Cat(Animal):
#覆盖了父类的方法
def shout(self):
print(‘miao’)
#覆盖本身的方法,调用父类的方法
def shout(self):
print(super())
print(super(Cat,self))
super().shout()
super(Cat,self).shout()   #等价于super()
# self.__class__.__bases__.shout(self)
a = Animal()
a.shout()
c =Cat()
c.shout()

print(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)

 

 

Animal shouts

<super: <class ‘Cat’>, <Cat object>>

<super: <class ‘Cat’>, <Cat object>>

Animal shouts

Animal shouts

{}

{}

{‘__module__’: ‘__main__’, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘shout’: <function Animal.shout at 0x0000001852BD4D08>, ‘__doc__’: None, ‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>}

{‘__module__’: ‘__main__’, ‘__doc__’: None, ‘shout’: <function Cat.shout at 0x0000001852BDB0D0>}

 

Super()可以访问到父类的属性。

 

class Animal:

@classmethod
def class_method(cls):
print(‘class_method_animal’)

@staticmethod
def static_method():
print(‘statice_method_animal’)

class Cat(Animal):
@classmethod
def class_method(cls):
print(‘class_method_cat’)

@staticmethod
def static_method():
print(‘statice_method_animal’)

c = Cat()
c.class_method()
c.static_method()

 

 

class_method_cat

statice_method_animal

 

这些都可以进行被覆盖,原因是字典的搜索顺序。

5、继承中的初始化

 

上图中类B定义时候生命继承自类A,则在类B中__base__中是可以看到A的

但是这是和调用类A的构造方法是两回事。

如果B中调用了A的构造方法,就可以拥有父类的属性了。

 

class A:
def __init__(self,a,b=1):
self.a = a
self.__b = b

class B(A):
def __init__(self,b,c):
A.__init__(self,b + c,b -c )
self.b = b
self.c = c

def printv(self):
print(self.b)
print(self.a)
f = B(4,5)
print(f.__dict__)
print(f.__class__.__bases__)
f.printv()

 

 

 

{‘c’: 5, ‘a’: 9, ‘_A__b’: -1, ‘b’: 4}

(<class ‘__main__.A’>,)

4

9在B中调用了父类的__init__函数。  如果父类中定义了__init__,子类中就应该调用他。

 

如果利用自动调用父类的__init__方法呢。

class A:
def __init__(self):
self.a1 = ‘a1’
self.__a2 = ‘a2’

class B:
pass
b =B()
print(b.__dict__)

 

 

子类没有自己的属性和方法,直接继承。。。会自动调用的。

 

此种类型不会自动调用,需要手动处理调用:

class A:
def __init__(self):
self.a1 = ‘a1’
self.__a2 = ‘a2’
print(‘Ainit’)
class B(A):
def __init__(self):
self.b1 =’b1′
print(‘B init’)
b =B()
print(b.__dict__)

 

B init

{‘b1’: ‘b1’}

正确初始化。

class A:
def __init__(self,age):
print(‘Ainit’)
self.age = age

def show(self):
print(self.age)

class B(A):
def __init__(self,age,weight):

print(‘Binit’)
self.age = age +1
self.weight = weight
super().__init__(age)
b = B(10,5)
b.show()

 

调用父类的__init__方法,出现在不同的位置,导致出现的结果不同。

 

class A:
def __init__(self,age):
print(‘Ainit’)
self.__age = age

def show(self):
print(self.__age)

class B(A):
def __init__(self,age,weight):

print(‘Binit’)
self.__age = age +1
self.__weight = weight
super().__init__(age)
b = B(10,5)
b.show()

 

打印出10,原因在__dict__,父类A的show方法中__age h会被解释为_A__age因此显示的就是10,而不是11.

自己私有的属性,就该自己读取和修改,不要借助其他类的方法,即使是父类或者派生类。

 

 

 

继承中的初始化

(继承加覆盖就是多态。)

 

三、多继承

1、Python不同的版本的类

Python2.2之前是没有共同祖先的,之后,引入object的类,他是所有类的共同 的祖先object。

Python2中为了兼容,分为古典类(旧式类)和新式类。

Python3中全部都是新式类。

新式类都是继承自object的,新式类可以使用super。

2、Ocp原则

多用继承,少用修改。

继承的用途,增强基类,实现多态。

 

多态:

在面向对象中,父类,子类通过继承联系在一起,如果通过一套方法,就可以实现不同的表现,就是多态。

一个类继承自多个类就是多继承,他将会有多个类的特征。

3、多继承弊端

多继承很好的模拟了世界,因为事物很少是单一继承的,但是舍弃简单,必然引入复杂性,带来了冲突。

 

如同一个孩子继承了来自父母双方的特征,那么到底眼睛像爸爸还是妈妈呢,孩子究竟该谁多一点呢。

 

多继承的实现会导致编译器设计的复杂度增加,所以很多语言舍弃了类的多继承。

 

C++支持多继承,JAVA也舍弃了多继承。

Java中,一个类可以实现多个接口,一个接口也可以继承多个解耦,Java的接口很纯粹,只是方法的生命。继承者必须实现这些方法,就具有了这些能力,就能干什么。

 

多继承可能会带来二义性,例如,猫和狗都继承自动物类,现在如果一个类多继承了猫和狗类,猫和狗都有shout方法,子类究竟继承谁的shout呢。

 

解决的方案。

实现多继承的语言,要解决二义性,深度优先或者广度优先。

4、Python多继承实现。

Class ClassName(基类列表)

类体

图片3

左图是多继承,右图是单一继承。

 

多继承带来路径选择问题,究竟继承哪个父类的特征呢。

 

Python使用MRO(method resolution order)解决基类搜索顺序问题。

历史原因:MRO有三个搜索算法:

经典算法,按照定义从左到右,深度优先策略,2.2之前左图的MRO是MYclass dbaca

新式算法,经典算法的升级,重复的是只保留最后一个,2.2左图的mro是myclassd,b,c,a,object

C3算法,在类被创建出来的时候,就计算出一个MRO有序列表,2.3之后,Python3唯一支持的算法。

左图中MRO是myclass,d,b,c,a,object的列表。

C3算法解决了多继承的二义性。

5、多继承的缺点

当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径。

Python语法是允许很多继承的,但Python代码是解释执行的,只有执行到的时候,才会发现错误。

 

团队协作开发,如果引入多继承,name代码将会不可控。

不管编程语言是否支持多继承,都应避免使用多继承。

 

6、Mixin类                  (继承加覆盖等于多态。)

类有下面的继承关系。

图片2

本质上是:多继承的来实现,体现的是组合的设计模式。

 

文档Document类是其他所有文档类的抽象基类;

Word PDF类是Document的子类。

 

为document子类提供打印能力:

方法:

1)在Document中提供print方法。

 

class Document:
def __init__(self,content):
self.content = content

def print(self):
raise NotImplementedError()

class Word(Document):pass
class Pdf(Document):pass

 

基类提供的方法不应该具体实现,因为他未必适合子类的打印,子类中需要覆盖重写。

Print是一种能力 ——打印功能,不是所有的Document的子类都需要,所有,从这个角度出发,会有问题的。

 

2)需要打印的子类上增加。

如果在现有子类上直接增加,违反了OCP原则,所以应该继承后增加。

图片1

##3采用多继承形式。

class Printable:
def print(self):
print(self.content)

class Document:
def __init__(self,content):
self.content = content

class Word(Document):pass
class Pdf(Document):pass

class PrintableWord(Printable,Word):pass
print(PrintableWord.__dict__)
print(PrintableWord.mro())

pw = PrintableWord(‘test str’)
pw.print()

 

{‘__doc__’: None, ‘__module__’: ‘__main__’}

[<class ‘__main__.PrintableWord’>, <class ‘__main__.Printable’>, <class ‘__main__.Word’>, <class ‘__main__.Document’>, <class ‘object’>]

test str

 

应用于网络,文档应该具备序列化的能力,类上就应该实现序列化。

可序列化还分为使用pickle,json,messagepack等

发现,类可能太多了,继承的方式很好。

功能太多,A需要几样功能,B需要几样功能,很繁琐。

3)装饰器。

使用装饰器增强一个类,把功能给类附加上去,那个类需要他,就去装饰他。

class Document:
def __init__(self,content):
self.content = content

class Word(Document):pass
class Pdf(Document):pass

def printable(cls):
cls.print = lambda self: print(self.content)
return lcs

@printable  #printableWord=printable(printableword)
class PrintableWord(Word):pass

a = PrintableWord()
a.print(‘ls’)

 

 

 

class Document:
def __init__(self,content):
self.content = content

class Word(Document):pass
class Pdf(Document):pass

def printable(cls):
def _print(self):
print(self.content)
cls.print = _print
return cls

@printable  #printableWord=printable(printableword)
class PrintableWord(Word):pass

a = PrintableWord()
a.print(‘ls’)

 

 

优点:简单方便,在需要的地方动态增加,直接使用装饰器。

4)Mixin

 

class Document:
def __init__(self,content):
self.content = content

class Word(Document):pass
class Pdf(Document):pass

class PrintableMixin:
def print(self):
print(self.content,’Mixin’)

class PrintableWord(PrintableMixin,Word):pass
print(PrintableWordMixin.__dict__)
print(PrintableWordMixin.mro())

def printable(cls):
def _print(self):
print(self.content)
cls.print = _print
return cls

@printable  #printableWord=printable(printableword)
class PrintableWord(Word):pass

pw = PrintableWord(‘test str’)
pw.print()

6、Mixin类(混合)  实现的是组合。解决的是应该的是缺少什么样的功能。,场景和使用范围是不一样的。不出现除了功能以外的属性方法等。(组合模式,)

 

Mixin本质上就是多继承。

Mixin体现的是一种组合的设计模式。

 

在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能来自不同的类提供,需要好多类组合在一起。设计角度说多组合,少继承。

 

Mixin类的使用原则。

不应该显示的出现__init__初始化方法。

通常不能单独工作,因为他是准备混入别的类中的功能的实现。

Mixin类的祖先类应该是Mixin类。

 

使用时候,Mixin类通常在继承列表的第一个位置,class PrintableWord(PrintableMixin,Word):pass

 

 

 

Mixin类和装饰器

这两种方式都可以使用,看个人喜好。

如果还需要继承就使用Mixin类的方式。

 

 

装饰器内容易冲突问题。

7、练习题:

1)shape基类,要求所有子类必须提供面积的计算,子类有三角形,矩形,圆。

import math

# class Shape:
#     def __init__(self,area):
#         self.area = area
#
# class Triangle(Shape):
#
#     def __init__(self,length,height,wide):
#         self.length = length
#         self.height = height
#         self.wide = wide
#         p = (self.length + self.height + self.wide) / 2
#         self.area = (p * (p – self.length) * (p – self.height) * (p – self.wide)) ** 0.5
#         print(self.area)
#
# class Rectangle(Shape):
#
#     def __init__(self,length,wide):
#         self.length = length
#         self.wide = wide
#         self.area = self.length * self.wide
#         print(self.area)
#
# class Circle(Shape):
#
#     def __init__(self,r):
#         self.r = r
#         self.area = self.r * self.r * math.pi
#         print(self.area)
#
# Rectangle(3,5)
# Circle(5)
# Triangle(3,5,7)

 

为了不改变类的__init__函数,所以需要继承的东西自定义为一个函数(方法)

import math

class Shape:
@property
def area(self):
return area
class Triangle(Shape):

def __init__(self,length,height,wide):
self.length = length
self.height = height
self.wide = wide

@property
def area(self):
p = (self.length + self.height + self.wide) / 2
return (p * (p – self.length) * (p – self.height) * (p – self.wide)) ** 0.5

class Rectangle(Shape):

def __init__(self,length,wide):
self.length = length
self.wide = wide

@property
def area(self):
return self.length * self.wide

class Circle(Shape):

def __init__(self,r):
self.r = r
@property
def area(self):
return self.r * self.r * math.pi

print(Circle(5).area)
print(Triangle(5,6,7).area)
print(Rectangle(5,6).area)

 

2)把上题圆类的数据可序列化

import math

class Shape:
@property
def area(self):
return area
class Triangle(Shape):

def __init__(self,length,height,wide):
self.length = length
self.height = height
self.wide = wide

@property
def area(self):
p = (self.length + self.height + self.wide) / 2
return (p * (p – self.length) * (p – self.height) * (p – self.wide)) ** 0.5

class Rectangle(Shape):

def __init__(self,length,wide):
self.length = length
self.wide = wide

@property
def area(self):
return self.length * self.wide

class Circle(Shape):

def __init__(self,r):
self.r = r
@property
def area(self):
return self.r * self.r * math.pi

print(Circle(5).area)
print(Triangle(5,6,7).area)
print(Rectangle(5,6).area)

import json
import msgpack
import pickle
class SeriviableMixin:
def dumps(self,m = ‘json’):
if m == ‘json’:
return dumps(self.__dict__)
elif m == ‘msgpack’:
return dumps(self.__dict__)
else:
return dumps(self.__dict__)

class SeriviableCircleMixin(SeriviableMixin,Circle):pass
a = SeriviableCircleMixin(4)
print(a)

 

 

利用property()

 

多继承:多路径的,mro多继承实现的,对象方法解析顺序,包括属性。     多态:继承加重写或者覆盖。

父类派生类,继承体系进行直接继承,不用再自己的内部实现,比如属性和方法。

四、二分

1、有一个无序序列[37,99,73,48,47,40,40,25,99,51],先进行排序打印输出,分别尝试插入20/40/41 数值到序列中合适的位置,保证其有序。

1)第一种实现,利用嵌套for循环,每次迭代出来的数值进行比较。如果比原表中的数值小,则插入到这个数左面。

 

 

 

lst1 = [37,99,73,48,47,40,40,25,99,51]
lst = sorted(lst1)

for x in (20,40,41,100):
i = 0
for i,v in enumerate(lst):
if v > x:
break
lst.insert(i,x)
print(lst)

 

 

2、利用二分法实现。

排序后二分查找到适当位置插入数值。

排序使用sorted解决,假设升序输出。

查找插入点,使用二分查找完成。

假设全长为n,首先在大致的中点元素开始和待插入的数进行比较,如果大则和右边的区域的中点元素进行比较,如果小则和左边的区域的中点进行比较,以此类推。

 

 

lst = [37, 99, 73, 48, 47, 40, 40, 25, 99, 51]
lst1 = sorted(lst)
def lst_insert(lst1,i):
ret = lst1
# print(ret)
length = len(lst1)
low = 0
while low < length:
mid = length // 2
if ret[mid] < i:
low = mid + 1       #说明i大,右边,限制下限。
else:
length = mid      #说明i不大于,左边,限制上限。
ret.insert(low,i)
print(ret)
for x in (40,20,21):
lst_insert(lst1,x)

 

 

算法的核心就是折半至重合为止。

 

 

3、二分

1)二分的前提是有序,否则不允许。

2)二分的查找算法的时间复杂度是O(log n)

4、Bisect模块

提供的函数有:

bisect.bisect_left(a,x,lo=0,hi=len(a)):

查找在有序列表a中插入x的index。Lo和hi用于指定列表的区间,默认是使用整个列表,如果x已经存在,在其左边插入,返回值为index。

 

bisect.bisect_right(a,x,lo= 0,hi=len(a))或者bisect.bisect(a,x,lo= 0,hi=len(a))

和bisect_left类似,但如果x已经存在,则在其右边插入。

 

bisect.insort_left(a,x,lo= 0,hi=len(a))

在有序列表a中插入x,等同于a.insert(bisect.bisect_left(a,x,lo,hi),x).

 

bisect.insort_right(a,x,lo= 0,hi=len(a))或者bisect.insort(a,x,lo= 0,hi=len(a))和insort_left函数类似,但是如果x如果已经存在,在其右边插入。

 

 

函数可以分为两类:

Bisect系,用于查找index。

Insort系,用于实际插入。

默认重复时候从右边插入。

 

import bisect

lst = [37, 99, 73, 48, 47, 40, 40, 25, 99, 51]
lst1 = sorted(lst)
print(lst1)  #[25, 37, 40, 40, 47, 48, 51, 73, 99, 99]
print(20,bisect.bisect(lst1,20))    #20 0
print(30,bisect.bisect(lst1,30))    #30 1
print(40,bisect.bisect(lst1,40))    # 40 4
print(100,bisect.bisect(lst1,100))    #  100 10

print(20,bisect.bisect_left(lst1,20))   #20 0
print(30,bisect.bisect_left(lst1,30))   #30 1
print(40,bisect.bisect_left(lst1,40))   #40 2
print(100,bisect.bisect_left(lst1,100))   # 100 10

for x in (20,30,40,100):
bisect.insort_left(lst1,x)
print(lst1)
# [20, 25, 37, 40, 40, 47, 48, 51, 73, 99, 99]
# [20, 25, 30, 37, 40, 40, 47, 48, 51, 73, 99, 99]
# [20, 25, 30, 37, 40, 40, 40, 47, 48, 51, 73, 99, 99]
# [20, 25, 30, 37, 40, 40, 40, 47, 48, 51, 73, 99, 99, 100]

5、二分应用。

判断学习成绩,成绩等级A-E  。90分以上为A ,80 以上为B  ,70以上为C  60以上为D。60以下为D。

 

def insert_score(nums):
lst = [60, 70, 80, 90]
grades = “EDCBA”
index = bisect.bisect(lst,nums)
print(index)
print(grades[index])
insert_score(70)

五、特殊属性:

1、属性

属性 含义
__name__ 类、函数、方法等的名字   __dir__
__module__ 类定义所在的模块名
__class__ 对象或类所属的类   只是返回基类
__bases__ 返回自己到object的类,类的基类元组,顺序为在基类列表中出现的顺序。
__doc__ 类,函数的文档字符串,如果没有定义则为None。
__mro__ 类的不是实例的。类的mro,class.mro()返回的结果保存在__mro__中。
__dict__ 类或实例属性,可写的字典。

标识符和名称两码回事。。

2、查看属性

方法 意义
__dir__(只是影响实例)*** 返回类或者对象的所有成员名称列表,dir()函数就是调用__dir__(),如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中收集信息。   收集很多信息

如果dir([object])参数obj包含方法__dir__(),该方法则被调用,如果参数obj中不包含__dir__(),该方法将最大限度的收集参数信息。

dir ()对不同类型的对象具有不同的行为。

如果对象是木块对象,返回的列表包含模块的属性名。

如果对象是类型或者类对象,返回的列表包含的属性名,以及他的基类的属性名。

否则,返回列表包含对象的属性名,他的类的属性名和类的基类的属性名。

 

import animal
from animal import Animal

class Cat(Animal):
x = ‘cat’
y =’abcd’

class Dog(Animal):
def __dir__(self):
return [‘dog’]

print(‘——-‘)
print(1,’current modules \’s names = {}’.format(dir()))     #模块名词空间内的属性。
print(2,’anmial modules \’s names = {}’.format(dir(animal)))   #指定模块内的属性。
print(3,”object ‘s __dict__  = {}”.format(sorted(object.__dict__.keys())))    #object 的字典。
print(4,”Animal’s dir()={}”.format(dir(Animal)))  #类Animal的dir()
print(5,”Cat’s dir() = {}”.format(dir(Cat)))    #类Cat的dir()
print(‘++++++++++++++++’)
tom = Cat(‘tom’)
print(6,sorted(dir(tom)))    #tom 的属性,cat类以及祖先类的属性。
print(7,sorted(tom.__dir__()))  #和上面一致,
print(8,sorted(set(tom.__dict__.keys())|set(Cat.__dict__.keys())|set(object.__dict__.keys())))

print(9,”Dog’s dir = {}”.format(dir(Dog)))
dog = Dog(‘snoppy’)
print(10,dir(dog))
print(11,dog.__dict__)

 

animal Module’s names = [‘Animal’, ‘__builtins__’, ‘__cached__’, ‘__doc__’, ‘__file__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’]

——-

1 current modules ‘s names = [‘Animal’, ‘Cat’, ‘Dog’, ‘__builtins__’, ‘__cached__’, ‘__doc__’, ‘__file__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’, ‘animal’]

2 anmial modules ‘s names = [‘Animal’, ‘__builtins__’, ‘__cached__’, ‘__doc__’, ‘__file__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’]

3 object ‘s __dict__  = [‘__class__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’]

4 Animal’s dir()=[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘x’]

5 Cat’s dir() = [‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘x’, ‘y’]

++++++++++++++++

6 [‘_Animal__age’, ‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘name’, ‘weight’, ‘x’, ‘y’]

7 [‘_Animal__age’, ‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘name’, ‘weight’, ‘x’, ‘y’]

8 [‘_Animal__age’, ‘__class__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘name’, ‘weight’, ‘x’, ‘y’]

9 Dog’s dir = [‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘x’]

10 [‘dog’]

11 {‘name’: ‘snoppy’, ‘_Animal__age’: 10, ‘weight’: 20}

3、魔术方法:***

分类:

创建、初始化与销毁

__init__   初始化时候调用。(做资源申请,属性的建立等,存放数据等。)

 

__del__   引用计数为0的时候。占用资源清理工作。

 

hash: (__eq__方法等等,hash函数,把数据散列,把原数据散列,输入一定,输出也是一定的。)  幂等性。不过hash多少次,hash只是作为门牌号码,值怎么放hash不管。去重的话用到了__eq__函数。

 

bool : (做等效处理,如果没有__bool__方法,会找取__len__方法,)

 

可视化:print()  __repr__  __str__   __bytes__

Str调用内建函数str,print ,format会调用魔术方法str。

没有提供str会使用repr,没有提供repr的会采用祖先类的。

 

 

运算符重载:极其有作用。

 

容器大小:可迭代等由其完成。In操作。  geitem 和元素有关。

 

可调用对象:

 

上下文管理:with as

 

反射:

 

描述器:

 

其他杂项:

4、hash   在实例调用的时候起作用。

__hash__ 内建函数hash()调用的返回值,返回一个整数。如果定了这个方法该类的实例就可hash。

class A:
def __init__(self,name,age=18):
self.name =name

def __hash__(self):
return 1

def __repr__(self):
return self.name

print(hash(A(‘tom’)))      #  1
print((A(‘tom’),A(‘tom’)))   #(tom, tom)
print([A(‘tom’),A(‘tom’)])    #[tom, tom]

s = {A(‘tom’),A(‘tom’)}      #{tom, tom}
print(s)
print({tuple(‘t’),tuple(‘t’)})    # {(‘t’,)}
print({(‘tom,’,),(‘tom’,)})    #{(‘tom’,), (‘tom,’,)}
print({‘tom’,’tom’})    #{‘tom’}

 

 

 

……..
class A:
def __init__(self,name,age=18):
self.name =name

def __hash__(self):
return 1

def __eq__(self, other):      等等函数作用
return self.name == other.name

def __repr__(self):
return self.name

print(hash(A(‘tom’)))      #  1
print((A(‘tom’),A(‘tom’)))   #(tom, tom)
print([A(‘tom’),A(‘tom’)])    #[tom, tom]

s = {A(‘tom’),A(‘tom’)}      #{tom}
print(s)
print({tuple(‘t’),tuple(‘t’)})    # {(‘t’,)}
print({(‘tom’,),(‘tom’,)})    #{(‘tom’,)}
print({‘tom’,’tom’})    #{‘tom’}

……………………………………………

hash 只是解决了数据放在哪里的问题(数据存放问题)。不解决两个值一样的处理问题。(不不比较值相等的问题。)

(只是门牌号码,存放多少个值就不一样。)

 

方法 意义
__eq__ 对应==操作符,判断两个对象是够相等,返回bool值

 

__hash__ = None

不可hash 就是以上的设置。

__hash__方法只是返回一个hash值作为set的key,但是去重还是需要__eq__来判断两个对象是否相等。

 

Hash值相等,只是hash冲突,不能说明两个值是相等的

因此,一般来说提供__hash__方法只是为了作为set或者dict的key,所以去重要求同时提供__eq__方法。

 

不可hash对象isinstance(p1,collections.hashable)一定为False。

去重需要提供__eq__方法。

 

练习:设计二维坐标系,使其成为可hash类型,比比较两个坐标的实例是否相等。

class Point:
def __init__(self,x,y):
self.x = x
self.y = y

# def finish(self):
#     return self.x,self.y
#
def __hash__(self):
return  hash((self.x,self.y))

def __eq__(self, other):
return self.x == other.x and self.y == other.y

def __repr__(self):
return ‘{}{}’.format(self.x,self.y)
p1 = Point(4,5)
p2 = Point(4,5)

 

为什么list类实例不可以hash呢。

因为源码中有一句__hash__ =None ,所以如果调用了__hash__就相当于调用了None,一定报错,如果一个类不能hash就把__hash__设置为None。

………………………………………………………………………….

5、bool

方法 意义
__bool__ 内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值,没有定义__bool__(),就找__len__()返回长度,非0为真,如果 __len__()也没有定义,那么实例就为真。

 

 

class A:pass
print(bool(A()))
if A():
print(‘real A’)

class B:
def __bool__(self):
return False

print(bool(B))
print(bool(B()))    False 因为实例化定义了为flase。

if B:
print(‘real B’)

class C:
def __len__(self):
return 0
print(bool(C))
if C:
print(‘real c’)

 

 

……………………………………………………………………….

可视化:

方法 意义
__repr__ 内建函数repr()对一个对象获取字符串表达,调用__repr__返回字符串表达,如果__repr__也没有定义,就直接返回object的定义就显示内存地址信息。
__str__ Str()函数,内建函数format(),print()函数调用,需要返回对象的字符串表达,如果没有定义,就去调用__repr__方法返回字符串表达,如果__repr__没有定义,就直接返回对象的内存地址信息。
__bytes__ bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象。

Print   format  str首先调用str方法。没有的话会找到__repr__。

 

class A:
def __init__(self,name,age=1):
self.name = name
self.age = age

def __repr__(self):
return ‘repr:{},{}’.format(self.name,self.age)

def __str__(self):
return ‘str:{},{}’.format(self.name,self.age)

def __bytes__(self):
return ‘{}is{}’.format(self.name,self.age).encode()

print(1,A(‘tom’))    #1 str:tom,1
print(2,[A(‘tom’)])   # 2 [repr:tom,1]
print(3,([str(A(‘tom’))]))    #3 [‘str:tom,1’]
print(4,bytes(A(‘tom’)))       #4 b’tomis1′
print(‘str:a,1’)

 

 

执行bytes 和list时候会报错,因为是在构建函数,后面的不是可迭代对象。

……………………………………………………………

6、运算符重载:

Operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作。

运算符 特殊方法 含义
<,<=,==,>,>=,!= __lt__,__le__,__eq__,__gt__,__ge__,__ne__ 比较运算符
+,-,*,/,%,//,**,divmod __add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__ 算数运算符,移位,位运算也有对应的方法。
+=,-=,*=,/=,%=,//=,**= __iadd__,__isub__,__imul__,__itruediv__,__imod__,__ifloordiv__,__ipow__

class A:
def __init__(self,name,age=15):
self.name = name
self.age = age

def __sub__(self, other):
return self.age – other.age

def __isub__(self, other):
return A(self.name,self – other)

tom = A(‘tom’)
jerry = A(‘jerry’,16)

print(tom – jerry)   #-1
print(tom – jerry,jerry.__sub__(tom))   #-1   1

 

6.1练习,完成point类设计,实现判断点相等的方法,并完成向量的加法。

class Point:
def __init__(self,x,y):
self.x = x
self.y = y

def __eq__(self, other):
return self.x == other.x and self.y == other.y

def __add__(self, other):
return Point(self.x+other.x,self.y+other.y)

def __str__(self):
return ‘{},{}’.format(self.x,self.y)

p1 = Point(1,1)
p2 = Point(2,2)
points = (p1,p2)
print(points[0]+points[1])

7、运算符重载应用场景

往往是面向对象实现的类,需要做大量的运算。Int类实现了所有操作符。

 

From functools import total_ordering

8、@functools.total_ordering   装饰器。

__lt__, __le__ ,__eq__, __gt__ ,__ge__ 是比较大小必须实现的方法啊,利用@functools.total_ordering 装饰器就可以大大简化代码。

使用装饰器的时候__eq__必须实现其他方法,等等,大于小于等实现其一就可以。

 

from functools import total_ordering

@total_ordering
class Person:
def __init__(self,name,age):
self.name = name
self.age = age

def __eq__(self, other):
return self.age == other.age

def __gt__(self, other):
return self.age > other.age

tom = Person(‘tom’,20)
jerry = Person(‘jerry’,18)

print(tom>jerry)    #True
print(tom<jerry)   ##False
print(tom==jerry)  # False
print(tom>=jerry)   #True
print(tom<=jerry)   #False

 

装饰器虽然大大简化代码。但是会带来性能问题,所以需要什么方法自己去创建就可以了。

 

一共六种比较,只是需要创建三样就可以了。

 

class Person:
def __init__(self,name,age):
self.name = name
self.age = age

def __eq__(self, other):
return self.age == other.age

def __gt__(self, other):
return self.age > other.age

def __ge__(self, other):
return self.age >= other.age

tom = Person(‘tom’,20)
jerry = Person(‘jerry’,18)

print(tom>jerry)    #True
print(tom<jerry)   ##False
print(tom==jerry)  #False
print(tom>=jerry)   #True
print(tom<=jerry)   #False
print(tom!=jerry)   #True

 

 

 

9、容器相关方法

方法 意义
__len__ 内建函数len(),返回对象的长度(>=0)的整数,如果把对象当做是容器类看,就是如同list和dict。Bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0为真。
__iter__ 迭代容器时候,调用,返回一个新的迭代器对象
__contains__ In 成员运算符,没有实现,就调用__iter__方法遍历
__getitem__ 实现self[key]访问,序列对象,key接受整数位索引,或者切片。对于set和dict,key为hashable,key不存在引发keyerror异常。
__setitem__ 和__getitem__的访问类似,是设置值得方法。
__missing__ 字典和其子类使用__getitem__() 调用时,key不存在执行该方法。

class A(dict):
def __missing__(self, key):
print(‘missing key:’,key)
return 0

a = A()
print(a[‘k’])      #missing key: k    0

9.1 为什么空字典,空字符串,空元组,空集合,空列表可以等效为False。

因为空的这些采用内建函数len,长度为0,所以等效为False。

9.2练习 :将购物车改造成方便操作的容器类

class Cart:
def __init__(self):
self.items = []

def __len__(self):
return len(self.items)

def __iter__(self):
return iter(self.items)

def additem(self,item):
self.items.append(item)

def __add__(self, other):
self.items.append(other)
return self

def __getitem__(self,index):
return self.items[index]

def __setitem__(self, key, value):
self.items[key] = value

def __str__(self):
return str(self.items)

cart = Cart()
cart.additem(1)
cart.additem(‘abc’)

print(len(cart))  #  2
print(bool(cart))   # True

for x in cart:
print(x)   #   1   abc

print(3 in cart)   #  False

print(cart[1])    #    abc

print(cart + 4 + 5 + 6)   #  [1, ‘abc’, 4, 5, 6]
print(cart.__add__(12).__add__(13))    #   [1, ‘abc’, 4, 5, 6, 12, 13]

 

 

__getitem__  列表和字典都是通过key访问。

必须记住;

 

__missing__是和字典相关的。

 

 

10、可调用对象。

__closure__闭包,

Callable可调用对象。

a()相当于是a.__call__()调用。

函数即对象,对象A加上()就是调用对象的__call__()方法。

方法 意义
__call__ 类中定义一个该方法,实例就可以像函数一样调用

 

class Point:
def __init__(self,x,y):
self.x = x
self.y = y

def __call__(self, *args, **kwargs):
return ‘{}:{}’.format(self.x,self.y)

p = Point(4,5)
print(p)   #  <__main__.Point object at 0x000000B420CB2898>
print(p())   #4:5

 

class Adder:
def __call__(self, *args, **kwargs):
ret = 0
for x in args:
ret += x
self.ret = ret
return ret

adder = Adder()
print(adder(4,5,6))   #15
print(adder.ret)   #15

10.1斐波那契数列。

class Fib:
def __init__(self):
self.items = [0,1,1]

def __call__(self,n):
l = len(self.items)
if n <= 0:
raise IndexError
elif n < len(self.items):
return self.items[n]

for i in range(l,n+1):
x = self.items[i-1] + self.items[i-2]
self.items.append(x)
return x

fib = Fib()
print(fib(10))

 

 

class Fib:

def __init__(self):
self.lst = [0,1,1]

def __call__(self, index):
return self[index]

def __len__(self):
return len(self.lst)

def __iter__(self):
return iter(self.lst)

def __getitem__(self, index):
if index <0:
raise IndexError
if index < len(self.lst):
return self.lst[index]

for i in range(len(self),index+1):
self.lst.append(self.lst[i-1]+self.lst[i-2])
return self.lst[index]

def __str__(self):
return str(self.lst)

fib = Fib()
print(fib(10))

 

 

11、上下文管理

文件IO操作可以对文件对象使用上下文管理,使用with..as语法。

With open(‘test’)as f:

Pass

 

class Point:
pass

with Point() as p:
pass

提示错误信息,因为没有__exit__这个属性。

12、上下文管理对象:

当一个对象同时实现了__enter__() 和__exit__()方法,他就属于上下文管理的对象。

方法 意义
__enter__ 进入与此对象相关的上下文,如果存在此方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上
__exit__ 退出与此对象相关的上下文。

 

import time
class Point:
def __init__(self):
print(‘init’)

def __enter__(self):
print(‘enter’)

def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)

with Point() as p:
print(‘to do ‘)
time.sleep(2)

 

实例化的时候,并不会调用enter,进入with语句块调用__enter__ 方法,然后执行语句体,最后离开with语句的时候会调用__exit__语句。

 

13、上下文管理的安全性

class Point:
def __init__(self):
print(‘init’)

def __enter__(self):
print(‘enter’)

def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)

with Point() as p:
raise SyntaxError(‘error’)
print(‘to do ‘)

 

 

可以看出在进入和退出的时候照样执行函数,上下文管理是安全的。

极端的例子就是采用退出当前解释器的函数,sys.exit()窗口直接关闭了,Python运行环境推出了,但是enter 和exit函数照样执行。

14、with语句。

class Point:
def __init__(self):
print(‘init’)

def __enter__(self):
print(‘enter’)

def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)
p = Point()
with p as f:
print(p == f)

 

 

p和f是不一样的,因为p是实例对象,f却是enter的返回值。

__enter__方法返回值就是上下文中使用的对象,with语法会把其返回值赋值给as子句的变量。

 

With 可以开启一个上下文运行的环境,在执行前做一些准备工作,执行后做一些收尾工作。

 

__enter__  进入

__exit__   退出     碰到with的时候才会调用。

首先创建实例先调用__init__

 

With  A( )as f:

f的值是__enter__的返回值。

 

F = A()

With  f:

f就是实例,

 

15、__enter__ 方法和 __exit__方法的参数。

__enter__ 的参数就是实例本身。

__exit__  的参数.一共是三个。如果退出时候没有异常,则这三个值 是None。

如果存在异常,参数意义如下:

exc_type ,异常类型。

exc_value,异常的值。

traceback 异常的追踪信息。

__exit__方法返回一个等效True的值,则会压制异常,否则,继续抛出异常。

 

class Point:
def __init__(self):
print(‘init’)

def __enter__(self):
print(‘enter’)

def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit’)
print(exc_type)   #<class ‘SyntaxError’>
print(exc_val)   #error
print(exc_tb)    #<traceback object at 0x000000E91743AE88>
return ‘abc’
p = Point()
with p as f:
raise SyntaxError(‘error’)
print(‘to do ‘)

15.1练习:

为加法函数计时。

第一种方法使用装饰器。

import datetime
import time

def timeit(fn):
def wrapper(*args,**kwargs):
start =datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now()-start).total_seconds()
print(‘{} took {}s’.format(fn.__name__,delta))
return ret
return wrapper

@timeit
def add(x,y):
time.sleep(2)
return x + y

print(add(3,4))

 

利用上下文实现:

import datetime
import time
from functools import wraps

def timeit(fn):
“””This is a fn”””
    @wraps(fn)
def wrapper(*args,**kwargs):
start =datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now()-start).total_seconds()
print(‘{} took {}s’.format(fn.__name__,delta))
return ret
return wrapper

@timeit
def add(x,y):
“””this is a add func”””
    time.sleep(2)
return x + y

print(add(3,4))

class Timeit:
def __init__(self,fn):
self.fn = fn

def __enter__(self):
self.start = datetime.datetime.now()
return self.fn

def __exit__(self, exc_type, exc_val, exc_tb):
delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{}took {}s’.format(self.fn.__name__,delta))

with Timeit(add)as fn:
print(add(4,7))

 

利用可调用对象来实现。

import datetime
import time
from functools import wraps

def timeit(fn):
“””This is a fn”””
    @wraps(fn)
def wrapper(*args,**kwargs):
start =datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now()-start).total_seconds()
print(‘{} took {}s’.format(fn.__name__,delta))
return ret
return wrapper

@timeit
def add(x,y):
“””this is a add func”””
    time.sleep(2)
return x + y

print(add(3,4))

class Timeit:
def __init__(self,fn):
self.fn = fn

def __enter__(self):
self.start = datetime.datetime.now()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{}took {}s’.format(self.fn.__name__,delta))

def __call__(self, x, y):
return self.fn(x,y)

with Timeit(add)as timeobject:
print(timeobject(4,7))

 

把类当做装饰器来实现

class Timeit:
def __init__(self,fn):
self.fn = fn

def __enter__(self):
self.start = datetime.datetime.now()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{}took {}s’.format(self.fn.__name__,self.delta))

def __call__(self, *args,**kwargs):
self.start = datetime.datetime.now()
ret = self.fn(*args,**kwargs)
self.delta = (datetime.datetime.now()-self.start).total_seconds()
print(‘{}took {}s call’.format(self.fn.__name__,self.delta))
return ret

@Timeit
def add(x,y):
“””this is a add func”””
    time.sleep(2)
return x + y

add(3,4)

 

解决文档字符串的问题:   实例的__doc__ =函数的__doc__

class Timeit:
def __init__(self,fn):
self.fn = fn
self.__doc__ = fn.__doc__

def __enter__(self):
self.start = datetime.datetime.now()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{}took {}s’.format(self.fn.__name__,self.delta))

def __call__(self, *args,**kwargs):
self.start = datetime.datetime.now()
ret = self.fn(*args,**kwargs)
self.delta = (datetime.datetime.now()-self.start).total_seconds()
print(‘{}took {}s call’.format(self.fn.__name__,self.delta))
return ret

@Timeit
def add(x,y):
“””this is a add func”””
    time.sleep(0.5)
return x + y

add(3,4)
print(Timeit(add).__doc__)    #this is a add func

print(add.__doc__)            #this is a add func

利用funtools工具。

import time
import datetime
from functools import wraps,update_wrapper

class Timeit:
“””this a class”””
    def __init__(self,fn):
self.fn = fn
# self.__doc__ = fn.__doc__     把函数对象的文档字符串直接赋给类
# update_wrapper(self,fn)
wraps(fn)(self)

def __enter__(self):
self.start = datetime.datetime.now()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.delta = (datetime.datetime.now() – self.start).total_seconds()
print(‘{}took {}s’.format(self.fn.__name__,self.delta))

def __call__(self, *args,**kwargs):
self.start = datetime.datetime.now()
ret = self.fn(*args,**kwargs)
self.delta = (datetime.datetime.now()-self.start).total_seconds()
print(‘{}took {}s call’.format(self.fn.__name__,self.delta))
return ret

@Timeit
def add(x,y):
“””this is a add func”””
    time.sleep(0.5)
return x + y

add(3,4)
print(Timeit(add).__doc__)
print(add.__doc__)

类即可以用在上下文管理,又可以用作装饰器。

 

 

 

 

 

16、上下文管理用用场景。

  • 增强功能。

在代码执行的前后增加代码,以增强其功能,类似装饰器的功能,

  • 资源管理。

打开资源需要关闭,例如文件对象,网络连接,数据库连接等。

  • 权限验证。  在执行代码之前,做权限的验证, __enter__时候管理。

 

上下文不管异常有多强,清理等依然进行处理。

 

……………………………………………………..分割线……………………………………………………

17、Contextlib.contextmanager。

他是一个装饰器实现上下文管理,装饰一个函数,而不用像类,一样实现__enter__和 __exit__方法。

对下面的函数有要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值,也就是这个装饰器接受一个生产期函数作为参数。

@contextlib.contextmanager
def foo():
print(‘enter’)   #相当于__enter__()
yield 100      #yield值只能有一个,作为__enter__的返回值,
print(‘exit’)   #相当于__exit__()

with foo() as f:
print(f)

 

as 后面的变量接的是yield语句返回的值。

@contextlib.contextmanager
def foo():
print(‘enter’)   #相当于__enter__()
try:
yield 100      #yield值只能有一个,作为__enter__的返回值,
finally:
print(‘exit’)   #相当于__exit__()

with foo() as f:
raise IndexError
print(f)

 

遇到异常依然会执行相应的语句。

import contextlib
import datetime
import time

@contextlib.contextmanager
def add(x,y):
start = datetime.datetime.now()
try:
yield x+y
finally:
delta = (datetime.datetime.now()-start).total_seconds()
print(delta)

with add(3,4) as f:
time.sleep(1)
print(f)

 

总结,如果业务逻辑简单可以使用函数加contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加__enter__  和__exit__ 方法方便。

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/98386

发表评论

登录后才能评论

联系我们

400-080-6560

在线咨询:点击这里给我发消息

邮件:1823388528@qq.com

工作时间:周一至周五,9:30-18:30,节假日同时也值班