Python3笔试实际操作基础3.md

时间:2022-07-28
本文章向大家介绍Python3笔试实际操作基础3.md,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1)Python笔试基础知识

  1. 问以下类定义中哪些是类属性,哪些是实例属性? 答:num 和 count 是类属性(静态变量),x 和 y 是实例属性;通常你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)。 class C: num = 0 #类属性 def __init__(self): self.x = 4 #实例属性 self.y = 5 C.count = 6
  2. 如何优雅地避免访问对象不存在的属性(不产生异常)?
  • 第一种先使用 hasattr(object, name) 函数判断属性是否存在
  • 第二种方法是直接使用 getattr(object, name[, default]) 函数并设置 default 参数(返回对象指定的属性值,如果指定的属性不存在,返回default(可选参数)的值)
  1. 你真的理解了修饰符的用法吗? 一个修饰符就是一个函数,它将被修饰的函数(再其下方)做为参数,并返回修饰后的同名函数或其它可调用的东西。@something def f(): print("I love FishC.com!") # 相当于 def f(): print("I love FishC.com!") f = something(f)
  2. 鸭子类型(duck typing) 鸭子类型是动态类型的一种风格,一个对象有效的语义不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定;

在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的; 鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。

注意事项:

  • 静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查
  • 从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性
  • DT类型应避免使用 type() 或 isinstance() 等测试类型是否合法
#!/usr/bin/python3
#鸭子类型案例了解

class Duck:
    def quack(self): 
        print("Duck 类 呱呱呱!")
    def feathers(self): 
        print("Duck 类  这个鸭子拥有灰白灰白的羽毛。")

class Person:
    def quack(self):
        print("Person 类 你才是鸭子你们全家人是鸭子!")
    def feathers(self): 
        print("Person 类  这个人穿着一件鸭绒大衣。")

def in_the_forest(duck):
    duck.quack()
    duck.feathers()

#入口
def game():
    '''Duck Typing 类型测试'''
    donald = Duck()
    john = Person()
    in_the_forest(donald)  #实际动态调用了Duck类定义的方法
    in_the_forest(john)

print("###########",game.__doc__,"#################")
game()


#案例2:鸭子类型
def calc(a, b, c):
        return (a + b) * c

>>> a = calc(1, 2, 3)
>>> b = calc([1, 2, 3], [4, 5, 6], 2)
>>> c = calc('love', 'FishC', 3)
>>> print(a)
9
>>> print(b)
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
>>> print(c)
loveFishCloveFishCloveFishC


########### Duck Typing 类型测试 #################
# Duck 类 呱呱呱!
# Duck 类  这个鸭子拥有灰白灰白的羽毛。
# Person 类 你才是鸭子你们全家人是鸭子!
# Person 类  这个人穿着一件鸭绒大衣。

总结:

  • 鸭子类型给予 Python 这样的动态语言以多态
  • 该方法即灵活,又提高了对程序员的要求
  • 多态的实现完全由程序员来约束强制实现(文档、清晰的代码和测试),并没有语言上的约束(如 C++ 继承和虚函数)
  1. Python 什么时候会调用到反运算的魔法方法? 答:例如 a + b,如果 a 对象的 add 方法没有实现或者不支持相应的操作,那么 Python 就会自动调用 b 的 radd 方法。
  2. 如何使用静态方法、类方法或者抽象方法?(后续了解)
  • Python中方法的运作:方法是作为类的属性(attribute)存储的函数;
  • 静态方法: 是一类特殊的方法。有时我们需要写属于一个类的方法,但是不需要用到对象本身
  • 类方法:是绑定在类而非对象上的方法!它总会被绑定在其归属的类上,同时它第一个参数是类本身(记住:类同样是对象)
  • 抽象方法在一个基类中定义,但是可能不会有任何的实现。在 Java 中,这被描述为一个接口的方法。
#!/usr/bin/python3
#[扩展阅读] 如何使用静态方法、类方法或者抽象方法

#1.Python 方法的运作
class Pizza(object):
    def __init__(self, size):
        self.size = size
    def get_size(self):
        return self.size

print(Pizza.get_size)  #Python3中 归属于一个类的函数不再被看成未绑定方法(unbound method),但是作为一个简单的函数(返回函数地址)
                       #Python2中 类Pizza的属性get_size是一个非绑定的方法

print("Pizza类地址:",Pizza(12).get_size)    #实例化对象与方法绑定,在 Python3 中bound原理是一样的,模型被简化了
print("get_size() 获取方法后值:",Pizza(12).get_size())  #实例化对象调用get-size方法

## 但是如何知道已绑定的方法被绑定在哪个对象上?技巧如下:
m = Pizza(42).get_size
print("Pizza对象",m.__self__,"nPizza对象get_size方法地址即是bound method Pizza.get_size 类地址:",m == m.__self__.get_size)

############################## 执行结果 #######################################
# <function Pizza.get_size at 0x000002BF0D9C9598>
# Pizza类地址: <bound method Pizza.get_size of <__main__.Pizza object at 0x000002BF0D9F2978>>
# get_size() 获取方法后值: 12
# Pizza对象 <__main__.Pizza object at 0x000002BF0D9F2978>
# Pizza对象get_size方法地址即是bound method Pizza.get_size地址: <bound method Pizza.get_size of <__main__.Pizza object at 0x000002BF0D9F2978>>


#2.静态方法
# 静态方法避免了这样的情况:
# - 降低了阅读代码的难度:看到 @staticmethod 便知道这个方法不依赖与对象本身的状态;
# - 允许我们在子类中重载mix_ingredients方法。
# 如果我们使用在模块最顶层定义的函数 mix_ingredients,一个继承自 SPizza 的类若不重载 cook,可能不可以改变混合成份(mix_ingredients)的方式。

class SPizza(object):
    @staticmethod   #@ 修饰器 
    def mix_ingredients(x, y):
        return x + y
    
    #将方法 mix_ingredients 作为一个非静态的方法也可以 work,但是给它一个 self 的参数将没有任何作用。
    def cook(self):  
        return self.mix_ingredients(self.cheese, self.vegetables)

print(SPizza().mix_ingredients(1,2))  # 3 
print(SPizza.cook is SPizza().cook)  # False 是因为前者是 类的方法  后者是实例化对象的方法嘛,

# >>> Pizza().mix_ingredients is Pizza().mix_ingredients
# True
# >>> Pizza().mix_ingredients is Pizza.mix_ingredients
# True

# >>> Pizza()
# <__main__.Pizza object at 0x10314b410>



#3.类方法
#类方法一般用于下面两种:
#- 工厂方法,被用来创建一个类的实例,完成一些预处理工作,如果我们使用一个 @staticmethod 静态方法,我们可能需要在函数中硬编码 Pizza 类的名称,使得任何继承自 Pizza 类的类不能使用我们的工厂用作自己的目的。
class Pizza(object):
    def __init__(self, ingredients):
        self.ingredients = ingredients

    @classmethod #类方法
    def from_fridge(cls, fridge):
        return cls(fridge.get_cheese() + fridge.get_vegetables())

#- 静态方法调静态方法:如果你将一个静态方法分解为几个静态方法,你不需要硬编码类名但可以使用类方法
class Pizza(object):
 def __init__(self, radius, height):
     self.radius = radius
     self.height = height

 @staticmethod  #静态方法
 def compute_circumference(radius):
      return math.pi * (radius ** 2)

 @classmethod  #类方法
 def compute_volume(cls, height, radius):
      return height * cls.compute_circumference(radius)  #可以调用静态方法

 def get_volume(self):
     return self.compute_volume(self.height, self.radius)


#4.抽象方法
class Pizza(object):
 def get_radius(self):
     raise NotImplementedError  #任何继承自 Pizza 的类将实现和重载 get_radius 方法,否则会出现异常。

# >>> Pizza()
# <__main__.Pizza object at 0x106f381d0>

# >>> Pizza().get_radius()
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 3, in get_radius
# NotImplementedError

#有种提前引起错误发生的方法,那就是当对象被实例化时,使用 Python 提供的 abc 模块。
import abc
class BasePizza(object):
 __metaclass__ = abc.ABCMeta

 @abc.abstractmethod
 def get_radius(self):
     """Method that should do something."""
# 使用 abc 和它的特类,一旦你试着实例化 BasePizza 或者其他继承自它的类,就会得到 TypeError:
# >>> BasePizza()
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: Can't instantiate abstract class BasePizza with abstract methods get_radius

总结:

  1. Python 不需要对每个实例化的 Pizza 对象实例化一个绑定的方法。(绑定方法同样是对象,创建它们需要付出代价)
  2. 静态方法是代码一运行就会分配内存地址的 不管怎么调用 实例化类 都不会变
  3. 类方法是调用才会分配地址 随调用随分配
  4. 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 的值也设置为那个值”

def 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 语句,如果我们发生一个真实的值,这时是没有人去“接收”它的。一旦生成器启动了,我们就可以像上面那样发送数据了。

案例:

import 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. 属性访问问题魔法方法
def __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


>>> dir(super) #注意超类没有 __getattr__ 方法
['__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. 属性访问/描述符案例
class 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
  1. Python 基于序列的三大容器类指的是什么吗? 无疑是列表(List),元组(Tuple)和字符串(String)啦, 读 —— getitem(),写 —— setitem(),删除 —— delitem(),”
  2. 迭代相关知识点
  • 迭代就是重复反馈过程的活动,其目的是为了接近并达到所需的目标或结果;
  • 迭代不是一个容器,且判断一个容器是不是有迭代功能只需要查看iter() 和 next() 方法
  • 迭代器不能回退到上一个值,只能一往无前(),当容器中无元素时候,就能抛出 StopIteration 异常表示容器已经没有元素了
  • Py原生的数据结构中(但对于无法随机访问的数据结构 )set只能使用迭代器进行访问;
  1. 模块的知识点总结
  • 模块就是程序一个.py文件就是一个独立的模块
  • 如果你不想模块中的某个属性被 from…import * 导入,那么你可以给你不想导入的属性名称的前边加上一个下划线(_)
  • 模块容易错点,执行下边 a.py 或 b.py 任何一个文件,都会报错
 # 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()
  1. Python 是支持常量需要进行导入const? 提示一:我们需要一个 Const 类 提示二:重写 Const 类的某一个魔法方法,指定当实例对象的属性被修改时的行为 提示三:检查该属性是否已存在 提示四:检查该属性的名字是否为大写 提示五:细心的鱼油可能发现了,怎么我们这个 const 模块导入之后就把它当对象来使用(const.NAME = “FishC”)了呢?

难道模块也可以是一个对象? 没错啦在 Python 中无处不对象,到处都是你的对象。使用以下方法可以将你的模块与类 A 的对象挂钩。

# 该模块用于让 Python 支持常量操作
class Const:    
    def __setattr__(self, name, value):
        if name in self.__dict__:
            raise TypeError('常量无法改变!')
            
        if not name.isupper():
            raise TypeError('常量名必须由大写字母组成!')

        self.__dict__[name] = value

'''
sys.modules 是一个字典,它包含了从 Python 开始运行起,被导入的所有模块。键就是模块名,值就是模块对象。
'''
import sys
sys.modules[__name__] = Const()


# 实际案例1:
# const 模块就是这道题要求我们自己写的
# const 模块用于让 Python 支持常量操作
import const

const.NAME = "FishC"  #定义一个常量
print(const.NAME)

try:
    # 尝试修改常量
    const.NAME = "FishC.com"
except TypeError as Err:
    print(Err)

try:
    # 变量名需要大写
    const.name = "FishC"
except TypeError as Err:
    print(Err)
  1. 生成器知识点总结
  • 所谓的协同程序就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始
  • 通过生成器来实现类似于协调程序的概念,生成器可以暂时挂起函数,并保留函数的局部变量等数据,然后在再次调用它的时候,从上次暂停的位置继续执行下去
  • 生成器所能实现的任何操作都可以由迭代器来代替,内部会创建iter和next方法;
  • 生成器的最大作用是可以“保留现场”,当下一次执行该函数是从上一次结束的地方开始,而不是重头再来。

案例:

#实现一个功能与 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]
  1. name 属性含义总结
  • 所有模块都有一个 name 属性,name 的值取决于如何应用模块,在作为独立程序运行的时候 name 属性的值是 ‘main‘,而作为模块导入的时候,这个值就是该模块的名字了
  • 可以通过 sys 模块中的 path 变量显示出来(sys.path)
  • 可以将模块文件放在 site-packages 文件夹之后就能直接进行调用;
  • 分一个文件夹是普通文件夹还是包,看文件夹中是否有 init.py 文件
  • 必须在包文件夹中创建一个 init.py 的模块文件,内容可以为空。可以是一个空文件,也可以写一些初始化代码。

2)Python操作实验

  1. 多重继承的陷阱:支持多继承的面向对象编程都可能会导致钻石继承(菱形继承)问题

WeiyiGeek.钻石继承

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 是所有类的基类,金字塔的顶端):

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

解决方法:你应该召唤 super 函数大显神威

class 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…
  1. 定义一个点(Point)类和直线(Line)类,使用 getLen 方法可以获得直线的长度。
#设点 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.0
  1. Python Mixin 编程机制学习说明? !important Mixin 编程是一种开发模式,是一种将多个类中的功能单元的进行组合的利用的方式,这听起来就像是有类的继承机制就可以实现,然而这与传统的类继承有所不同。 通常 Mixin 并不作为任何类的基类,也不关心与什么类一起使用,而是在运行时动态的同其他零散的类一起组合使用。

Mixin 机制特点:

  • 可以在不修改任何源代码的情况下,对已有类进行扩展;
  • 可以保证组件的划分;
  • 可以根据需要,使用已有的功能进行组合,来实现“新”类;
  • 很好的避免了类继承的局限性,因为新的业务需要可能就需要创建新的子类。

Mixin 机制实现方法:

  • 多继承 (引用该类的模块都收到影响/不安全)
  • 插件方式

插件方式:通常我们希望修改对象的行为,而不是修改类的。同样的我们可以利用dict来扩展对象的方法。

#!/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.
  1. 类魔术方法实现定时器以及定时器相加,Python 具备一个叫做 timeit 的完美计时工具;#!/usr/bin/python3 #功能:采用魔术方法实现定时器 import time as t class MyTimer:
#!/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. 采用类属性访问方式进行设置描述符,实现华氏度与摄氏度之间的转换
#!/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. 魔法方法的利用
#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
  1. 根据课堂上的例子,定制一个列表,同样要求记录列表中每个元素被访问的次数。这一次我们希望定制的列表功能更加全面一些,比如支持 append()、pop()、extend() 原生列表所拥有的方法。你应该如何修改呢?

要求1:实现获取、设置和删除一个元素的行为(删除一个元素的时候对应的计数器也会被删除) 要求2:增加 counter(index) 方法,返回 index 参数所指定的元素记录的访问次数 要求3:实现 append()、pop()、remove()、insert()、clear() 和 reverse() 方法(重写这些方法的时候注意考虑计数器的对应改变).

class 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()