1)Python笔试基础知识
问以下类定义中哪些是类属性,哪些是实例属性?
答:num 和 count 是类属性(静态变量),x 和 y 是实例属性;通常你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)。1
2
3
4
5
6class C:
num = 0 #类属性
def __init__(self):
self.x = 4 #实例属性
self.y = 5
C.count = 6如何优雅地避免访问对象不存在的属性(不产生异常)?
- 第一种先使用 hasattr(object, name) 函数判断属性是否存在
- 第二种方法是直接使用 getattr(object, name[, default]) 函数并设置 default 参数(返回对象指定的属性值,如果指定的属性不存在,返回default(可选参数)的值)
- 你真的理解了修饰符的用法吗?
一个修饰符就是一个函数,它将被修饰的函数(再其下方)做为参数,并返回修饰后的同名函数或其它可调用的东西。1
2
3
4
5
6
7
8
9
def f():
print("I love FishC.com!")
# 相当于
def f():
print("I love FishC.com!")
f = something(f)
- 鸭子类型(duck typing)
鸭子类型是动态类型的一种风格,一个对象有效的语义不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定;
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的; 鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
注意事项:
- 静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查
- 从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性
- DT类型应避免使用 type() 或 isinstance() 等测试类型是否合法
1 | #!/usr/bin/python3 |
总结:
- 鸭子类型给予 Python 这样的动态语言以多态
- 该方法即灵活,又提高了对程序员的要求
- 多态的实现完全由程序员来约束强制实现(文档、清晰的代码和测试),并没有语言上的约束(如 C++ 继承和虚函数)
- Python 什么时候会调用到反运算的魔法方法?
答:例如 a + b,如果 a 对象的 add 方法没有实现或者不支持相应的操作,那么 Python 就会自动调用 b 的 radd 方法。
- 如何使用静态方法、类方法或者抽象方法?(后续了解)
- Python中方法的运作:方法是作为类的属性(attribute)存储的函数;
- 静态方法: 是一类特殊的方法。有时我们需要写属于一个类的方法,但是不需要用到对象本身
- 类方法:是绑定在类而非对象上的方法!它总会被绑定在其归属的类上,同时它第一个参数是类本身(记住:类同样是对象)
- 抽象方法在一个基类中定义,但是可能不会有任何的实现。在 Java 中,这被描述为一个接口的方法。
1 | #!/usr/bin/python3 |
总结:
- Python 不需要对每个实例化的 Pizza 对象实例化一个绑定的方法。(绑定方法同样是对象,创建它们需要付出代价)
- 静态方法是代码一运行就会分配内存地址的 不管怎么调用 实例化类 都不会变
- 类方法是调用才会分配地址 随调用随分配
- yield 与 Generators(生成器)
最近几年,生成器的功能变得越来越强大,它已经被加入到了 PEP;在协程(coroutine),协同式多任务处理(cooperative multitasking),以及异步 IO(asynchronous I/O)中广泛使用;
(1) 协程(协同程序)与子例程
调用一个普通的 Python 函数时,结束于 return 语句、异常或者函数结束(可以看作隐式的返回 None),函数中做的所有工作以及保存在局部变量中的数据都将丢失;
函数需要能够“保存自己的工作”,这时便是yield出现的最佳时期;
- return 隐含的意思是函数正将执行代码的控制权返回给函数被调用的地方
- yield 的隐含意思是控制权的转移是临时和自愿的,我们的函数将来还会收回控制权。
当生成器函数调用 yield,生成器函数的“状态”会被冻结,所有的变量的值会被保留下来,下一行要执行的代码的位置也会被记录,调用一次next()就指向下一个yield位置(永远不会退回指向)。
while 循环是用来确保生成器函数永远也不会执行到函数末尾的,只要调用 next() 这个生成器就会生成一个值(引出了一个处理无穷序列的常见方法(这类生成器也是很常见的));
当 yield 关键字返回 number 的值,而像 other = yield foo 这样的语句的意思是,“返回 foo 的值,这个值返回给调用者的同时将 other 的值也设置为那个值”1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def get_primes(number):
while True:
if is_prime(number):
number = yield number #如普通函数return一样将值返回给调用则
number += 1
def print_successive_primes(iterations, base=10):
prime_generator = get_primes(base)
prime_generator.send(None) #必须调用设置None才能 通过 send 方法来将一个值“发送”给生成器。
for power in range(iterations):
print(prime_generator.send(base ** power)) #发生send值
# 首先我们打印的是 generator.send 的结果是OK的,因为 send 在发送数据给生成器的同时还返回生成器通过 yield 生成的值(就如同生成器中 yield 语句做的那样)。
# 看一下 prime_generator.send(None) 这一行,当你用 send 来“启动”一个生成器时(就是从生成器函数的第一行代码执行到第一个 yield 语句的位置),你必须发送 None。这
# 不难理解,根据刚才的描述,生成器还没有走到第一个 yield 语句,如果我们发生一个真实的值,这时是没有人去“接收”它的。一旦生成器启动了,我们就可以像上面那样发送数据了。
案例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45import random
def get_data():
"""返回0到9之间的3个随机数"""
return random.sample(range(10), 3)
def consume():
"""显示每次传入的整数列表的动态平均值"""
running_sum = 0
data_items_seen = 0
while True:
data = yield #生成器接收点 关键点
data_items_seen += len(data) # 每次调用值将会保留,下次执行的时候将会调用该值
running_sum += sum(data) #
print('The running average is {}'.format(running_sum / float(data_items_seen)))
def produce(consumer):
"""产生序列集合,传递给消费函数(consumer)"""
while True:
data = get_data()
print('Produced {}'.format(data))
consumer.send(data) #关键点 #通过 send 方法来将一个值“发送”给生成器。
yield #设置生成器
if __name__ == '__main__':
consumer = consume()
consumer.send(None) #启动生成器
producer = produce(consumer)
for _ in range(3):
print('Producing...')
next(producer)
########### 执行结果 ################
# 传递进去的值不会随着函数接收而消失,而是暂时进行了保存(以供下次使用);
# Producing...
# Produced [0, 9, 8]
# The running average is 5.666666666666667
# Producing...
# Produced [2, 3, 1]
# The running average is 3.8333333333333335
# Producing...
# Produced [3, 5, 2]
# The running average is 3.666666666666666
注意事项:
- 协程就是生成器,正式的术语叫生成器而已;
- 一个生成器函数的定义很像一个普通的函数,除了当它要生成一个值的时候,使用 yield 关键字而不是 return;
- 生成器就是一类特殊的迭代器,所以生成器必须要定义一些方法(method),其中一个就是 next()
- generator 是用来产生一系列值的,yield 则像是 generator 函数的返回结果
- yield 唯一所做的另一件事就是保存一个 generator 函数的状态,generator 就是一个特殊类型的迭代器(iterator)
- 和迭代器相似,我们可以通过使用 next() 来从 generator 中获取下一个值;通过隐式地调用 next() 来忽略一些值
- 属性访问问题魔法方法
1
2
3
4
5
6
7
8
9def __setattr__(self, name, value):
self.name = value + 1 #每当属性被赋值的时候, __setattr__() 会被调用,而里边的 self.name = value + 1 语句又会再次触发 __setattr__() 调用,导致无限递归。
self.__dict__[name] = value + 1 #方法1
super().__setattr__(name, value+1) #方法2
#注意超类没有 __getattr__ 方法 dir(super)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__self_class__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__thisclass__']WeiyiGeek.错误根源
- 属性访问/描述符案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39class Counter:
def __init__(self):
super().__setattr__('counter', 0)
def __setattr__(self, name, value):
super().__setattr__('counter', self.counter + 1) #关键点
super().__setattr__(name, value)
def __delattr__(self, name):
super().__setattr__('counter', self.counter - 1) #关键点
super().__delattr__(name)
#值得学习
class MyDes:
def __init__(self, value = None):
self.val = value
def __get__(self, instance, owner):
return self.val ** 2
class Test(MyDes):
x = MyDes(3)
#注意不能下面这样进行调用
def __init__(self):
self.x = myDes(3)
test = Test()
print(test.x) # 9
######### 属性访问 ##############
# >>> c = Counter()
# >>> c.x = 1
# >>> c.counter
# 1
# >>> c.y = 1
# >>> c.z = 1
# >>> c.counter
# 3
# >>> del c.x
# >>> c.counter
# 2
- Python 基于序列的三大容器类指的是什么吗?
无疑是列表(List),元组(Tuple)和字符串(String)啦, 读 —— getitem(),写 —— setitem(),删除 —— delitem(),”
- 迭代相关知识点
- 迭代就是重复反馈过程的活动,其目的是为了接近并达到所需的目标或结果;
- 迭代不是一个容器,且判断一个容器是不是有迭代功能只需要查看iter() 和 next() 方法
- 迭代器不能回退到上一个值,只能一往无前(),当容器中无元素时候,就能抛出 StopIteration 异常表示容器已经没有元素了
- Py原生的数据结构中(但对于无法随机访问的数据结构 )set只能使用迭代器进行访问;
- 模块的知识点总结
- 模块就是程序一个.py文件就是一个独立的模块
- 如果你不想模块中的某个属性被 from…import * 导入,那么你可以给你不想导入的属性名称的前边加上一个下划线(_)
- 模块容易错点,执行下边 a.py 或 b.py 任何一个文件,都会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39# a.py
from b import y
def x():
print('x')
# b.py
from a import x
def y():
print('y')
#这个是循环嵌套导入问题,都会抛出 ImportError 异常
Traceback (most recent call last):
File "/Users/FishC/Desktop/a.py", line 1, in <module>
from b import x
File "/Users/FishC/Desktop/b.py", line 1, in <module>
import a
File "/Users/FishC/Desktop/a.py", line 1, in <module>
from b import x
ImportError: cannot import name 'x'
#这是因为在执行其中某一个文件(a.py)的加载过程中,会创建模块对象并执行对应的字节码。但当执行第一个语句的时候需要导入另一个文件(from b import y),因此 CPU 会转而去加载另一个文件(b.py)。同理,执行另一个文件的第一个语句(from a import x)恰好也是需要导入之前的文件(a.py)
# 执行 b.py -> import a -> 查找 a 模块 -> 未发现 a 模块对象 -> 导入 a.py -> import b -> 查找 b 模块 -> 发现 b 模块对象 -> 接着往下执行字节码(import a 已执行过,Python 有机制确保不重复导入,因而不会再执行) -> a.x() -> 在 a 模块中找不到 x(),因为 a 还没有被完全导入嘛……
#解决方法
# a.py
import b
def x():
print('x')
if __name__ == "__main__":
b.y()
# b.py
import a
def y():
print('y')
if __name__ == "__main__":
a.x()
- Python 是支持常量需要进行导入const?
提示一:我们需要一个 Const 类
提示二:重写 Const 类的某一个魔法方法,指定当实例对象的属性被修改时的行为
提示三:检查该属性是否已存在
提示四:检查该属性的名字是否为大写
提示五:细心的鱼油可能发现了,怎么我们这个 const 模块导入之后就把它当对象来使用(const.NAME = “FishC”)了呢?
难道模块也可以是一个对象?
没错啦在 Python 中无处不对象,到处都是你的对象。使用以下方法可以将你的模块与类 A 的对象挂钩。
1 | # 该模块用于让 Python 支持常量操作 |
- 生成器知识点总结
- 所谓的协同程序就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始
- 通过生成器来实现类似于协调程序的概念,生成器可以暂时挂起函数,并保留函数的局部变量等数据,然后在再次调用它的时候,从上次暂停的位置继续执行下去
- 生成器所能实现的任何操作都可以由迭代器来代替,内部会创建iter和next方法;
- 生成器的最大作用是可以“保留现场”,当下一次执行该函数是从上一次结束的地方开始,而不是重头再来。
案例:1
2
3
4
5
6
7
8
9
10#实现一个功能与 reversed() 相同(内置函数 reversed(seq) 是返回一个迭代器,是序列 seq 的逆序显示)的生成器
for i in myRev("FishC"):
print(i, end='')
ChsiF
def myRev(data):
# 这里用 range 生成 data 的倒序索引
# 注意,range 的结束位置是不包含的
for index in range(len(data)-1, -1, -1):
yield data[index]
- name 属性含义总结
- 所有模块都有一个 name 属性,name 的值取决于如何应用模块,在作为独立程序运行的时候 name 属性的值是 ‘main‘,而作为模块导入的时候,这个值就是该模块的名字了
- 可以通过 sys 模块中的 path 变量显示出来(sys.path)
- 可以将模块文件放在 site-packages 文件夹之后就能直接进行调用;
- 分一个文件夹是普通文件夹还是包,看文件夹中是否有 init.py 文件
- 必须在包文件夹中创建一个 init.py 的模块文件,内容可以为空。可以是一个空文件,也可以写一些初始化代码。
2)Python操作实验
Q:钻石继承(菱形继承)会带来什么问题?
多重继承容易导致钻石继承(菱形继承)问题,上边代码实例化 D 类后我们发现 A 被前后进入了两次。
Q:这有什么危害?
我举个例子,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。
Q:如何避免钻石继承(菱形继承)问题?
为解决这个问题,Python 使用了一个叫“方法解析顺序(Method Resolution Order,MRO)”的东西,还用了一个叫 C3 的算法。
解释下 MRO 的顺序基本就是:在避免同一类被调用多次的前提下,使用广度优先和从左到右的原则去寻找需要的属性和方法。
在继承体系中,C3 算法确保同一个类只会被搜寻一次。
例子中如果一个属性或方法在 D 类中没有被找到,Python 就会搜寻 B 类,然后搜索 C类,如果都没有找到,会继续搜索 B 的基类 A,如果还是没有找到,则抛出“AttributeError”异常。
使用 类名.mro 获得 MRO 的顺序(注:object 是所有类的基类,金字塔的顶端):1
2 D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
解决方法:你应该召唤 super 函数大显神威1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33class A():
def __init__(self):
print("进入A…")
print("离开A…")
class B(A):
def __init__(self):
print("进入B…")
super().__init__() #超类
print("离开B…")
class C(A):
def __init__(self):
print("进入C…")
super().__init__() #超类
print("离开C…")
class D(B, C):
def __init__(self):
print("进入D…")
super().__init__() #超类
print("离开D…")
############## 执行结果 ###############
# >>> d = D()
# 进入D…
# 进入B…
# 进入C…
# 进入A…
# 离开A…
# 离开C…
# 离开B…
# 离开D…
定义一个点(Point)类和直线(Line)类,使用 getLen 方法可以获得直线的长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54#设点 A(X1,Y1)、点 B(X2,Y2),则两点构成的直线长度 |AB| = √((x1-x2)^2+(y1-y2)^2)
# Python 中计算开根号可使用 math 模块中的 sqrt 函数
# 直线需有两点构成,因此初始化时需有两个点(Point)对象作为参数
#!/usr/bin/python3
#方法1:继承
import math
class Point:
#初始化坐标
def __init__(self,x1=0,y1=0,x2=0,y2=0):
self.X1 = x1
self.X2 = x2
self.Y1 = y1
self.Y2 = y2
class Line(Point):
msg = ''
def __init__(self,msg,x1, y1, x2, y2):
super().__init__(x1, y1, x2, y2)
self.msg = msg
print("坐标信息 (%d,%d), (%d,%d)" %(self.X1,self.Y1,self.X2,self.Y2))
def getLen(self):
print("当前Tips:%s,计算中....." %self.msg)
return math.sqrt((self.X1 - self.X2) * (self.X1 - self.X2) + (self.Y1 - self.Y2) * (self.Y1 - self.Y2))
zhixian = Line('计算两点之间距离',1,1,4,5)
print(zhixian.getLen())
#方法2:传入对象类似于继承(值得学习)
class Point1():
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
class Line1():
def __init__(self, p1, p2):
self.x = p1.getX() - p2.getX()
self.y = p1.getY() - p2.getY()
self.len = math.sqrt(self.x*self.x + self.y*self.y)
def getLen(self):
return self.len
############## 执行结果 ###############
# 坐标信息 (1,1), (4,5)
# 当前Tips:计算两点之间距离,计算中.....
# 5.0Python Mixin 编程机制学习说明?
!important
Mixin 编程是一种开发模式,是一种将多个类中的功能单元的进行组合的利用的方式,这听起来就像是有类的继承机制就可以实现,然而这与传统的类继承有所不同。
通常 Mixin 并不作为任何类的基类,也不关心与什么类一起使用,而是在运行时动态的同其他零散的类一起组合使用。
Mixin 机制特点:
- 可以在不修改任何源代码的情况下,对已有类进行扩展;
- 可以保证组件的划分;
- 可以根据需要,使用已有的功能进行组合,来实现“新”类;
- 很好的避免了类继承的局限性,因为新的业务需要可能就需要创建新的子类。
Mixin 机制实现方法:
- 多继承 (引用该类的模块都收到影响/不安全)
- 插件方式
插件方式:通常我们希望修改对象的行为,而不是修改类的。同样的我们可以利用dict来扩展对象的方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51#!/usr/bin/python3
#功能:采用字典的方式来扩展对象方法 (值得学习)
class PlugIn(object): #注意这里的参数
def __init__(self):
self._exported_methods = []
def plugin(self, owner): #owner 即 我们combine 实例化的对象
for f in self._exported_methods:
print("类方法名 :"+f.__name__," 类方法地址:",f) #实际是AFeature/BFeature 方法名/方法地址地址
owner.__dict__[f.__name__] = f #在实例化对象c的字典之中加入将其他类方法
def plugout(self, owner):
for f in self._exported_methods:
del owner.__dict__[f.__name__]
#注意这里是扩展的Plugin类
class AFeature(PlugIn):
def __init__(self):
super(AFeature, self).__init__() #使用超类进行父类初始化
self._exported_methods.append(self.get_a_value) #初始化的时候讲本类的方法地址传入到父类的列表之中 (重点)
def get_a_value(self):
print("c可以被调用 a feature.")
#注意这里是扩展的Plugin类
class BFeature(PlugIn):
def __init__(self):
super(BFeature, self).__init__()
self._exported_methods.append(self.get_b_value)
def get_b_value(self):
print("c可以被调用 b feature.")
class Combine:
pass
c = Combine()
print("Plugin 中 owner 即是传入的 c 对象地址:",c)
AFeature().plugin(c) #作为插件 将A类中方法通过插件 加入到 c 对象中
BFeature().plugin(c) #同上
c.get_a_value()
c.get_b_value()
#######################执行结果###########################
# >python demo3.6.py
# Plugin 中 owner 即是传入的 c 对象地址: <__main__.Combine object at 0x000001D024DBDC18>
# 类方法名 :get_a_value 类方法地址: <bound method AFeature.get_a_value of <__main__.AFeature object at 0x000001D024DBDCC0>>
# 类方法名 :get_b_value 类方法地址: <bound method BFeature.get_b_value of <__main__.BFeature object at 0x000001D024DBDC88>>
# c可以被调用 a feature.
# c可以被调用 b feature.
- 类魔术方法实现定时器以及定时器相加,Python 具备一个叫做 timeit 的完美计时工具;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113#!/usr/bin/python3
#功能:采用魔术方法实现定时器
import time as t
class MyTimer:
def __init__(self):
self.prompt = "未开始计时!"
self.lasted = 0.0
self.begin = 0
self.end = 0
self.default_timer = t.perf_counter
def __str__(self):
return self.prompt
__repr__ = __str__
def __add__(self, other):
result = self.lasted + other.lasted
prompt = "总共运行了 %0.2f 秒" % result
return prompt
# 开始计时
def start(self):
self.begin = self.default_timer()
self.prompt = "提示:请先调用 stop() 停止计时!"
print("计时开始...")
# 停止计时
def stop(self):
if not self.begin:
print("提示:请先调用 start() 进行计时!")
else:
self.end = self.default_timer()
self._calc()
print("计时结束!")
# 内部方法,计算运行时间
def _calc(self):
self.lasted = self.end - self.begin
self.prompt = "总共运行了 %0.2f 秒" % self.lasted
# 为下一轮计时初始化变量
self.begin = 0
self.end = 0
# 设置计时器(time.perf_counter() 或 time.process_time())
def set_timer(self, timer):
if timer == 'process_time':
self.default_timer = t.process_time
elif timer == 'perf_counter':
self.default_timer = t.perf_counter
else:
print("输入无效,请输入 perf_counter 或 process_time")
a = MyTimer()
b = MyTimer()
a.Start()
b.Start()
t.sleep(5)
a.Stop()
b.Stop()
print("a 对象", a," b 对象" ,b)
print(a + b)
# 实现程序运行计时 (实例化的时候传入 func)
class FuncTimer:
def __init__(self, func, number=1000000):
self.prompt = "未开始计时!"
self.lasted = 0.0
self.default_timer = t.perf_counter
self.func = func
self.number = number
def __str__(self):
return self.prompt
__repr__ = __str__
def __add__(self, other):
result = self.lasted + other.lasted
prompt = "总共运行了 %0.2f 秒" % result
return prompt
# 内部方法,计算运行时间
def timing(self):
self.begin = self.default_timer()
for i in range(self.number):
self.func()
self.end = self.default_timer()
self.lasted = self.end - self.begin
self.prompt = "总共运行了 %0.2f 秒" % self.lasted
# 设置计时器(time.perf_counter() 或 time.process_time())
def set_timer(self, timer):
if timer == 'process_time':
self.default_timer = t.process_time
elif timer == 'perf_counter':
self.default_timer = t.perf_counter
else:
print("输入无效,请输入 perf_counter 或 process_time")
######## 执行结果 #############
# 计时开始...
# 计时开始...
# 计时停止!
# 计时停止!
# a 对象 总共运行了5秒 b 对象 总共运行了5秒
# 总共运行了10秒
采用类属性访问方式进行设置描述符,实现华氏度与摄氏度之间的转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39#!/usr/bin/python3
#温湿度类实现:摄氏度,华氏度
#摄氏度类
class Celsius:
#初始化构造方法
def __init__(self,value = 26.0):
self.value = float(value)
def __get__(self,instance,owner):
return self.value
def __set__(self,instance,value):
self.value =float(value)
#华氏度类
class Fahrenheit:
def __get__(self,instance,owner):
return instance.cel * 1.8 + 32 #返回华氏度 关键点
def __set__(self,instance,value):
instance.cel = (float(value) - 32) / 1.8 #返回摄氏度 关键点
#温度类
class Tempareture:
cel = Celsius() #摄氏度类
fah = Fahrenheit() #华氏度类
temp = Tempareture()
temp.cel = 37.5 #进行赋值的时候 cel 对象 value 属性 = 37.5
print("摄氏度 :%.2f , 华氏度 :%.2f" %(temp.cel,temp.fah)) #temp.fah 进行请求触发 华氏度类 __get__ => 返回华氏度的计算后的值
temp.fah = 75.0
print("摄氏度 :%.2f , 华氏度 :%.2f" %(temp.cel,temp.fah))
############ 执行结果 #############
# 摄氏度 :37.50 , 华氏度 :99.50
# 摄氏度 :23.89 , 华氏度 :75.00魔法方法的利用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41#1. 只需要重载 __lshift__ 和 __rshift__ 魔法方法即可
class Nstr(str):
def __lshift__(self, other):
return self[other:] + self[:other] #值得学习 LED 液晶显示平
def __rshift__(self, other):
return self[-other:] + self[:-other] #
############### 执行结果 #############
# >>> a = Nstr('I love FishC.com!')
# >>> a << 3
# 'ove FishC.com!I l'
# >>> a >> 3
# 'om!I love FishC.c'
#2. 定义一个类 Nstr,当该类的实例对象间发生的加、减、乘、除运算时,将该对象的所有字符串的 ASCII 码之和进行计算:
# 注意我们必须要用到 __new__ 方法,因为 str 是不可变类型
# 所以我们必须在创建的时候将它初始化
class Nstr(int):
def __new__(cls, arg=0):
if isinstance(arg, str):
total = 0
for each in arg:
total += ord(each) #转换成ascii码
arg = total
return int.__new__(cls, arg) #返回实例初始化处理后的结果
############### 执行结果 #############
# >>> a = Nstr('FishC')
# >>> b = Nstr('love')
# >>> a + b
# 899
# >>> a - b
# 23
# >>> a * b
# 201918
# >>> a / b
# 1.052511415525114
# >>> a // b
# 1根据课堂上的例子,定制一个列表,同样要求记录列表中每个元素被访问的次数。这一次我们希望定制的列表功能更加全面一些,比如支持 append()、pop()、extend() 原生列表所拥有的方法。你应该如何修改呢?
要求1:实现获取、设置和删除一个元素的行为(删除一个元素的时候对应的计数器也会被删除)
要求2:增加 counter(index) 方法,返回 index 参数所指定的元素记录的访问次数
要求3:实现 append()、pop()、remove()、insert()、clear() 和 reverse() 方法(重写这些方法的时候注意考虑计数器的对应改变).1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49class CountList(list):
def __init__(self, *args):
super().__init__(args)
self.count = []
for i in args:
self.count.append(0)
def __len__(self):
return len(self.count)
def __getitem__(self, key):
self.count[key] += 1
return super().__getitem__(key)
def __setitem__(self, key, value):
self.count[key] += 1
super().__setitem__(key, value)
def __delitem__(self, key):
del self.count[key]
super().__delitem__(key)
def counter(self, key):
return self.count[key]
def append(self, value):
self.count.append(0)
super().append(value)
def pop(self, key=-1):
del self.count[key]
return super().pop(key)
def remove(self, value):
key = super().index(value)
del self.count[key]
super().remove(value)
def insert(self, key, value):
self.count.insert(key, 0)
super().insert(key, value)
def clear(self):
self.count.clear()
super().clear()
def reverse(self):
self.count.reverse()
super().reverse()