Python 技能提升(二)

理想的类结构

在这里插入图片描述

@Property装饰器

# 传统写法
class Square1:
    def __init__(self):
        self.__side = None

    def get_side(self):
        return self.__side

    def set_side(self, side):
        assert side >= 0, '边长不能为负数!'
        self.__side = side

    def del_side(self):
        # del self.__side
        self.__side = 0  # 这里并不打算真的去删除


# Pythonic写法
class Square2:
    def __init__(self):
        self.__side = None

    @property
    def side(self):
        return self.__side

    @side.setter
    def side(self, side):
        assert side >= 0, '边长不能为负数!'
        self.__side = side

    @side.deleter
    def side(self):
        # del self.__side
        self.__side = 0  # 这里并不打算真的去删除


s = Square2()
s.side = 10
print(s.side)  # 10
del s.side
print(s.side)  # 0

公有化与私有化

# 在Python类里面,"属性和方法"默认都是公有的(public)
class Person:
    def __init__(self, name: str, age: int):
        self._name = name  # _ 类似与 C++ 的 protected,特点:_name 可以在类内部使用,也可以被继承
        self.__age = age  # __ 类似与 C++ 的 private,特点:__age 可以在类内部使用,但不可以被继承

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

    @name.setter
    def name(self, name):
        self._name = name

    @property
    def age(self):
        return self.__age

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


class Teacher(Person):  # 继承Person类

    @property
    def info(self):
        return self._name, self.__age  # 报错,不存在 __age 属性


t = Teacher('Jack', 35)
print(t.info)

属性控制

# 属性控制,可用于拦截或日志
class Person:
    # 控制获取属性
    def __getattribute__(self, item):
        print(f'getting attribute [{item}]')
        return super().__getattribute__(item)  # 调用父级(object)的方法

    # 控制设置属性
    def __setattr__(self, key, value):
        print(f'setting attribute [{key}] to [{value}]')
        super().__setattr__(key, value)  # 调用父级(object)的方法


person = Person()
person.name = 'Jack'  # 触发"__setattr__"方法
print(person.name)  # 触发"__getattribute__"方法

"""
运行结果:

setting attribute [name] to [Jack]
getting attribute [name]
Jack

"""

MRO

在这里插入图片描述

"""
MRO: Method Resolution Order(方法解析顺序)
作用:用于类的继承树的方法搜索顺序

在单继承的情况下,我们可以很好的理解"类的继承树的方法顺序"
但是在多继承下,"类的继承树的方法顺序"就显得尤为复杂了(在 Python3 里面,使用了C3线性化的算法进行排序)
通常情况下,我们可以使用类的 mro() 方法来直接查看"类的继承树的方法顺序"
"""


class A(object):
    pass


class B(object):
    pass


class C(object):
    pass


class D(object):
    pass


class E(object):
    pass


class K1(C, A, B):
    pass


class K2(A, D):
    pass


class K3(B, D, E):
    pass


class Z(K1, K2, K3):
    pass


print(Z.mro())
# [
# <class '__main__.Z'>,
# <class '__main__.K1'>,
# <class '__main__.C'>,
# <class '__main__.K2'>,
# <class '__main__.A'>,
# <class '__main__.K3'>,
# <class '__main__.B'>,
# <class '__main__.D'>,
# <class '__main__.E'>,
# <class 'object'>
# ]

可迭代对象

# 利用迭代对象进行迭代,本质是用时间换取空间
# 在迭代进行的那一刻,数据才被产生或处理,从而大幅降低了内存空间的占用

print('\n-----------------------------')

# 可迭代对象的迭代
lst = [66, 77, 88]
for num in lst:
    print(num, end=' ')

print('\n-----------------------------')

# 上面 for 循环的迭代原理如下
iter_obj = lst.__iter__()
print(iter_obj.__next__(), end=' ')
print(iter_obj.__next__(), end=' ')
print(iter_obj.__next__(), end=' ')

print('\n-----------------------------')


# 自定义可迭代对象
class Fibonacci:  # 斐波那契数
    """
    F0 = 0
    F1 = 1
    Fn = Fn-1 + Fn-2 (n >= 2)
    0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, ...
    """

    def __init__(self, n):
        self.__n = n
        self.__previous = 0
        self.__current = 1
        self.__count = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.__count >= self.__n:
            raise StopIteration

        self.__count += 1
        return_value = self.__previous
        self.__previous, self.__current = self.__current, self.__previous + self.__current
        return return_value


# 自定义可迭代对象的迭代
for num in Fibonacci(12):
    print(num, end=' ')

print('\n-----------------------------')

"""
运行结果:

-----------------------------
66 77 88 
-----------------------------
66 77 88 
-----------------------------
0 1 1 2 3 5 8 13 21 34 55 89 
-----------------------------

"""

__getitem__()方法

"""
在创建一个可迭代的对象序列时,可以用__iter__()和__next__()方法
如果没有这两个方法,我们还可以通过__getitem__()方法和__len__()方法进行创建序列
如果__getitem__()方法和__len__()方法也没有的话,解释器会抛出 TypeError 的错误!


而__getitem__()方法与上面的__iter__()方法正好相反
它是以空间换取时间:
    1.先占用内存,布置好数据
    2.随机查找的时间复杂度 O(1)
"""

from datetime import date, timedelta


class DateRange:
    def __init__(self, start_dt, end_dt):
        self.start_dt = start_dt
        self.end_dt = end_dt
        self._range_values = self._get_range_values()

    def _get_range_values(self):
        data = []

        current_dt = self.start_dt
        while current_dt <= self.end_dt:
            data.append(current_dt)
            current_dt += timedelta(days=1)
        return data

    def __len__(self):
        print("You are using the method of len().")
        return len(self._range_values)

    def __getitem__(self, index):
        print("You are using the symbol of [] to get item.")
        return self._range_values[index]


my_date_range = DateRange(date(2024, 6, 1), date(2024, 6, 3))
print(len(my_date_range))

for my_date in my_date_range:
    print(my_date)

"""
迭代原理:
在 DateRange 类中,尽管我们没有显式地定义__iter__(),但由于定义了__getitem__和__len__,因此你仍然可以在for循环中使用my_date_range对象。
Python 会自动为我们处理迭代过程,使用你定义的__getitem__来获取日期,直到索引超出由__len__指定的范围。
总结起来,for循环在 Python 中确实是通过迭代对象来工作的,但迭代对象不一定需要显式地定义__iter__()。
如果对象定义了__getitem__和__len__(并且表现出序列的特性),Python将为你提供一个隐式的迭代器,使得你可以在for循环中使用该对象。



运行结果:

You are using the method of len().
3
You are using the symbol of [] to get item.
2024-06-01
You are using the symbol of [] to get item.
2024-06-02
You are using the symbol of [] to get item.
2024-06-03
You are using the symbol of [] to get item.

"""

__contains__()方法

class Product:  # 产品
    def __init__(self, name: str, price: float, num: int) -> None:
        self.name = name
        self.price = price
        self.num = num


class Promotion:  # 促销方案
    def __init__(self, lower_num: int, upper_num: int, rate: float) -> None:
        self.__lower_num = lower_num
        self.__upper_num = upper_num
        self.__rate = rate

    @property
    def rate(self) -> float:
        return self.__rate

    def __contains__(self, product: Product) -> bool:
        return self.__lower_num <= product.num <= self.__upper_num


def get_total_price(products: [Product], promotions: [Promotion]) -> float:
    total_price = 0

    for product in products:
        promotion = [promotion for promotion in promotions if product in promotion][0]  # in 触发 __contains__()方法
        total_price += product.price * promotion.rate

    return total_price


if __name__ == '__main__':
    top_promotion = Promotion(100, 199, 0.5)  # 最好促销方案
    average_promotion = Promotion(50, 99, 0.8)  # 一般促销方案
    none_promotion = Promotion(0, 49, 1.0)  # 没有优惠的促销方案

    promotions = (top_promotion, average_promotion, none_promotion)

    products = (
        Product('cart', 89188.90, 188),
        Product('computer', 7999.99, 66),
        Product('toy', 13.60, 27),
    )

    total_price = round(get_total_price(products, promotions), 2)
    print(total_price)  # 51009.04

动态处理对象属性

class Person:
    def __init__(self, name: str) -> None:
        self.name = name

    def __getattribute__(self, attr):
        print("All operations that get attribute trigger this function to execute.")
        return super().__getattribute__(attr)

    def __getattr__(self, attr):
        print(f"You're trying to get attribute [{attr}], but the attribute is not defined.")
        return '空空如也'

    def __setattr__(self, key, value):
        print(f"You are setting the [{key}] attribute to [{value}].")
        super().__setattr__(key, value)


person = Person('Jack')
print(person.name)
# You are setting the [name] attribute to [Jack].
# All operations that get attribute trigger this function to execute.
# Jack

person.age = 33
print(person.age)
# You are setting the [age] attribute to [33].
# All operations that get attribute trigger this function to execute.
# 33


# 如果尝试获取没有定义过的属性,会触发  __getattr__() 方法
print(person.hobby)
# All operations that get attribute trigger this function to execute.
# You're trying to get attribute [hobby], but the attribute is not defined.
# 空空如也

__call__()方法

class MyPrint:

    def __call__(self, text: str, end=''):
        print(text, end=end)
        return self


my_print = MyPrint()
print(callable(my_print))  # True
my_print("你好呀")("我很好")("那就好")("都很好")  # 你好呀我很好那就好都很好

__str__()__repr__()

class Person:
    def __init__(self, name: str, hobby: [str]) -> None:
        self.name = name
        self.hobby = hobby

    def __str__(self):
        return f"{self.name} like {','.join(self.hobby)}"

    def __repr__(self):
        return f"Person(name={self.name}, hobby={self.hobby}"


person = Person('Jack', ['football', 'basketball'])
print(person)  # 调用 __str__()方法
print(str(person))  # 调用 __str__()方法
print(repr(person))  # 调用 __repr__()方法

"""
__str__()方法是用于普通人群、客户看
__repr__()方法是用于机器、开发人员看

运行结果:

Jack like football,basketball
Jack like football,basketball
Person(name=Jack, hobby=['football', 'basketball']

"""

深拷贝

import copy

list1 = [1, 2, 3, [4, 5]]
list2 = copy.deepcopy(list1)

print(list1, id(list1))
print(list2, id(list2))

# [1, 2, 3, [4, 5]] 2016821662784
# [1, 2, 3, [4, 5]] 2016821293952

import copy


class A:
    def __repr__(self):
        return f"A: {id(self)}"


class B:
    def __init__(self, a: A) -> None:
        self.a = a
        self.b = self
        self.c = 1

    def __repr__(self):
        return f"B: {id(self.a)}, {id(self.b)}, {id(self.c)}"


a1 = A()
b1 = B(a1)

memo = {}
b2 = copy.deepcopy(b1, memo=memo)

print(a1)
print(b1)
print(b2)

b2.c = 3

print(a1)
print(b1)
print(b2)

"""
运行结果:(内存地址)

A: 2124853485392
B: 2124853485392, 2124853487120, 140716077503272
B: 2124853487056, 2124851868688, 140716077503272
A: 2124853485392
B: 2124853485392, 2124853487120, 140716077503272
B: 2124853487056, 2124851868688, 140716077503336

"""

抽象类与方法

from abc import abstractmethod, ABCMeta


# 抽象类
class Component(metaclass=ABCMeta):

    @abstractmethod
    def bind_data(self):  # 抽象方法
        pass

    @abstractmethod
    def render(self):  # 抽象方法
        pass


# 具体类
class ProductListComponent(Component):

    def bind_data(self):  # 具体实现
        return ['computer', 'life']

    def render(self):  # 具体实现
        for item in self.bind_data():
            print(f'--- {item.upper()} ---')


product_list_component = ProductListComponent()
product_list_component.render()

数据类

"""
我们知道,在C/C++/C#语言里面,都有结构体Struct,它作为数据的载体,我们使用它处理数据很方便
在python里面,同样提供了类似的东西,就是数据类————@dataclass
数据类,在Python3.7版本开始以标准库方式提供

为什么要用数据类呢?
因为使用普通的类进行数据的封装与处理,往往需要经过很多繁琐且无意义的工作,比如使用__init__()方法声明数据等等
而使用数据类,将能帮助我们减少这些繁琐的操作,将精力集中在处理数据本身上
"""


class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

    def __repr__(self) -> str:
        return f"{self.name}({self.age})"


obj1 = Person("Jack", 28)
obj2 = Person("John", 35)
print(obj1)
print(obj2)
print(obj1 == obj2)
# Jack(28)
# John(35)
# False

from dataclasses import dataclass


@dataclass
class People:
    name: str
    age: int


obj1 = People("Jack", 28)
obj2 = People("John", 35)
print(obj1)
print(obj2)
print(obj1 == obj2)
# People(name='Jack', age=28)
# People(name='John', age=35)
# False

官方文档:https://docs.python.org/zh-cn/3/library/dataclasses.html

元类

# 所有类的祖宗————type

# int类
num = 3
print(type(num))
print(type(type(num)))
print(type(type(type(num))))
# <class 'int'>
# <class 'type'>
# <class 'type'>


# str类
name = "Jack"
print(type(name))
print(type(type(name)))
print(type(type(type(name))))
# <class 'str'>
# <class 'type'>
# <class 'type'>


# bool类
flag = True
print(type(flag))
print(type(type(flag)))
print(type(type(type(flag))))
# <class 'bool'>
# <class 'type'>
# <class 'type'>


# Object类
print(type(object))
print(type(type(object)))


# <class 'type'>
# <class 'type'>


# 自定义类
class MyClass:
    pass


obj = MyClass()

print(type(obj))
print(type(type(obj)))
print(type(type(type(obj))))
print('=' * 16)
# <class '__main__.MyClass'>
# <class 'type'>
# <class 'type'>

# 通过类的祖宗type创建一个类(类对象)
# 1. 定义类里面要实现的方法
def __repr__(self):
    return f"{self.__class__.__name__}(wheel={self.wheel}, type={self.size})"


# 2. 通过type()创建类对象
# --- 参数: ( 类的名称、(要继承的父类...)、{ 要定义的属性: ... } )
Vehicle = type('Vehicle', (), {'wheel': 4, '__repr__': __repr__, })
Bus = type('Bus', (Vehicle,), {'size': 'big', })
Truck = type('Truck', (Vehicle,), {'size': 'extremely big', })

# 类的实例
bus = Bus()
truck = Truck()
print(bus)
print(truck)
# Bus(wheel=4, type=big)
# Truck(wheel=4, type=extremely big)

class MetaAutoAttributes(type):
    def __new__(cls, name, bases, attrs):
        # 在这里我们可以修改或添加attrs中的属性
        attrs['auto_attribute'] = 'This is an automatically added attribute'
        # 调用父类的__new__方法来创建类
        return super().__new__(cls, name, bases, attrs)

    # 使用自定义元类


class MyClass(metaclass=MetaAutoAttributes):
    pass


# 创建MyClass的实例
obj = MyClass()

# 访问自动添加的属性
print(obj.auto_attribute)  # 输出: This is an automatically added attribute

推荐阅读:https://www.cnblogs.com/JetpropelledSnake/p/9094103.html

函数装饰器

import time


def timer(func):
    start = time.perf_counter()  # 统计时间
    func()
    end = time.perf_counter()
    print(f"耗时:{end - start}")


def show():
    print('showing...')
    time.sleep(0.5)


timer(show)

# showing...
# 耗时:0.5006642000516877

import time


def timer(func):
    start = time.perf_counter()  # 统计时间
    res = func()
    end = time.perf_counter()
    print(f"耗时:{end - start}")
    return res


def show():
    print('showing...')
    time.sleep(0.5)
    return "OK"


result = timer(show)
print(result)

# showing...
# 耗时:0.5005822000093758
# OK

import time


def timer(func):
    def inner(*args, **kwargs):
        start = time.perf_counter()  # 统计时间
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"耗时:{end - start}")
        return res

    return inner


def show(name: str) -> str:
    print(f'{name} is showing...')
    time.sleep(0.5)
    return "OK"


show = timer(show)
result = show('Jack')
print(result)
print(show.__name__)  # 不完美的伪装

# Jack is showing...
# 耗时:0.5001411000266671
# OK
# inner

import time
import functools


def timer(func):
    @functools.wraps(func)  # 完美伪装
    def inner(*args, **kwargs):
        start = time.perf_counter()  # 统计时间
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"耗时:{end - start}")
        return res

    return inner


def show(name: str) -> str:
    print(f'{name} is showing...')
    time.sleep(0.5)
    return "OK"


show = timer(show)
result = show('Jack')
print(result)
print(show.__name__)  # 完美伪装

# Jack is showing...
# 耗时:0.5001411000266671
# OK
# show

import time
import functools


def timer(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        start = time.perf_counter()  # 统计时间
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"耗时:{end - start}")
        return res

    return inner


@timer  # 使用语法糖
def show(name: str) -> str:
    print(f'{name} is showing...')
    time.sleep(0.5)
    return "OK"


# 不再使用原生的 show = timer(show) ,而是使用语法糖
result = show('Jack')
print(result)
print(show.__name__)

# Jack is showing...
# 耗时:0.5002126999897882
# OK
# show

含参装饰器

import time
import functools


def timer(appearance=False):  # 是否开启外观模式,默认不开启
    def outer(func):
        @functools.wraps(func)
        def inner(*args, **kwargs):
            if appearance:
                print("=" * 16)

            start = time.perf_counter()  # 统计时间
            res = func(*args, **kwargs)
            end = time.perf_counter()
            print(f"耗时:{end - start}")

            if appearance:
                print("=" * 16)

            return res

        return inner

    return outer


@timer(appearance=True)
def show(name: str) -> str:
    print(f'{name} is showing...')
    time.sleep(0.5)
    return "OK"


result = show('Jack')
print(result)

# ================
# Jack is showing...
# 耗时:0.5003457000711933
# ================
# OK

类装饰器

# 装饰功能:给被装饰的类,添加两个方法
class Entity:
    def __call__(self, cls):
        # 声明第一个方法
        def __repr__(self) -> str:
            return f'{cls.__qualname__}'  # __qualname__ 用于获取类或函数的完全限定名称,即包括模块名称和嵌套结构

        # 声明第二个方法
        def is_adult(self) -> bool:
            return self.age > 18

        # 给被装饰的类,添加上面两个方法
        cls.__repr__ = __repr__
        cls.is_adult = is_adult

        return cls


@Entity()  # Entity() 实例化一个对象.  对象 + __call__() 方法 == 函数()
class User:

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


# 本质:User = obj(User)
# 本质:User = cls

user = User('Jack', 35)
print(user)
print(user.is_adult())
# User
# True

上下文管理器

# 传统操作
file = open('test.txt', 'w')
try:
    file.write('This is a test file.')
except Exception as e:
    print(e)
finally:
    file.close()
    
# 利用上下文管理器with操作
with open('test.txt', 'w') as file:
    file.write('This is a test file.')

# 查看是否已经关闭文件(资源)
print(file.closed)  # True

# 自定义上下文管理器
class FileManager:
    def __init__(self, filename: str, mode: str) -> None:
        self.filename = filename
        self.mode = mode
        self.file = None  # 初始化文件对象

    def __enter__(self):
        print('>>> Open', self.filename)
        self.file = open(self.filename, self.mode)
        print(">>> file object:", self.file)  # 测试
        return self.file

    def __exit__(self, error_type, error_value, traceback):
        if self.file:
            print('>>> Close', self.filename)
            self.file.close()


with FileManager('test.txt', 'r') as file:
    print(">>> file object:", file)  # 测试
    print("==========<File Content>==========")
    print(file.read())
    print("==========<File Content>==========")


# >>> Open test.txt
# >>> file object: <_io.TextIOWrapper name='test.txt' mode='r' encoding='cp936'>
# >>> file object: <_io.TextIOWrapper name='test.txt' mode='r' encoding='cp936'>
# ==========<File Content>==========
# This is a test file.
# ==========<File Content>==========
# >>> Close test.txt

class Sequence:  # 顺序-sequence
    def __init__(self, start: int, end: int) -> None:
        self.start = start
        self.end = end

    def __enter__(self):
        self.data = list(range(self.start, self.end))
        return self.data

    # 参数:异常类型,异常值,异常回溯。 返回值:True(停止异常继续往外抛出)
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 判断是否为索引越界的错误类型
        if isinstance(exc_val, IndexError):  # isinstance(66, int) ==> 66 是不是 int 类的实例(对象),如果是,返回值True,如果不是,返回值False
            print(f'索引超出范围:[{self.start}, {self.end})')
            return True


with Sequence(1, 10) as seq:
    print(seq[5])  # 6
    print(seq[15])  # 索引超出范围:[1, 10)

import time


class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        self.end = 0

        def elapsed():
            return self.end - self.start

        return elapsed

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.perf_counter()


with Timer() as timer:
    print('Start to do sth.')
    time.sleep(1)
    print('Stop to do sth.')

print("运行时间:", timer())

# Start to do sth.
# Stop to do sth.
# 运行时间: 1.0006713000002492

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值