目录
- Python基础
- 注意事项
- 一、2023/4/4更新
- 前言
- 1.class
- 1.1 基本介绍
- 1.2 类的实例化
- 1.3 魔法函数
- 2.生成器
- 2.1 定义
- 2.2 生成器函数
- 2.3 生成器表达式
- 3.迭代器和可迭代对象
- 3.1 迭代器
- 3.2 可迭代对象
- 3.3 区别和联系
- 4.装饰器
- 4.1 定义和语法
- 4.2 类作为装饰器
- 4.2.1 形式1
- 4.2.2 形式2
- 4.3 修饰类的成员函数
- 4.4 常见装饰器
- 4.5 基于装饰器的分配机制
- 5.作业
- 5.1 作业7
- 5.2 作业8
- 总结
Python基础
注意事项
一、2023/4/4更新
经杜老师纠正,生成器只是一个技术,不要强制说生成器可以节省内存空间(节省内存low b说法,很掉价🤣)。下面是杜老师的解释:
生成器,是一种《设计模式》的实践,它与内存无关。
即使不使用生成器,正常的程序开发也不会出现傻乎乎的产生全部数据再处理。
1.生成器,代表的是一种范式,一种规范的实践
2.生成器,还有send语法,可以实现协程的作用
可参考协程与异步IO
这里同时扩展了《设计模式》这件事,它引导着OOP(面向对象编程)的开发进程。指导了很多新时代语言,它是软件开发和计算机基础很重要的内容。设计模式(比如之前yolo.cu中的接口模式就是一种设计模式),相当于一种思想,指导如何写程序,设计结构。
不同的语言(python、c++、java)都只是不同的语言规范,实现相通的思想,那就是设计模式或者编程范式。
设计模式可参考设计模式|菜鸟教程
前言
手写AI推出的全新保姆级从零手写自动驾驶CV课程,链接。记录下个人学习笔记,仅供自己参考。
本次课程主要学习类及其基本概念以及生成器、迭代器和装饰器的相关知识
课程大纲可看下面的思维导图。
1.class
1.1 基本介绍
Python中的类(即class)是一种面向对象的编程工具,可以用来描述和创建对象的模板。类中定义了对象的属性和行为,可以实现封装、继承和多态等面向对象编程的特性。(from chatGPT)
类的语法结构如下:
class ClassName:'''类的帮助文档字符串'''# 类属性,是类的所有实例共有的属性class_attribute = valuedef __init__(self, arg1, arg2, ...):'''初始化方法,创建实例时被调用'''# 实例属性,每个类都有自己的属性self.instance_attribute = value# 调用父类初始化方法super().__init__(arg1, arg2, ...)def instance_method(self, arg1, arg2, ...):'''实例方法,操作实例的属性'''pass@classmethoddef class_method(cls, arg1, arg2, ...):'''类方法,操作类的属性'''pass@staticmethoddef static_method(arg1, arg2, ...):'''静态方法,不涉及实例和类,一般用来实现工具函数'''pass
其中,
- 类属性是类的所有实例共有的属性,通过类名可以直接访问(比如Animal.life)
- 实例属性是每个实例有自己的属性,通过
self
关键字访问(比如dog.name) - 实例方法是操作实例的方法,必须有
self
作为第一个参数(比如dog.eat()) - 类方法是操作类的方法,必须有
cls
作为第一个参数,可以通过类名或实例名调用 - 静态方法是不涉及实例和类的方法,可以通过类名或实例名调用
类的继承通过在类定义时在括号内指定父类,如:
class SubClass(ParentClass):'''子类定义'''
子类可以继承父类的属性和方法,并可以在子类中添加新的属性和方法,实现代码的复用和扩展。
1.2 类的实例化
实例化是指,根据类创建一个实际的对象。类是抽象的概念,而对象则是具体的实现。比如说类Animal
,动物类这是一个抽象的概念,自然界有那么多动物,具体是那种动物呢?可以通过将其实例化赋予抽象类Animal
特点,让它变成具体的实现,比如dog
,它就是抽象类Animal
的一种实例对象。
在Python中,要实例化一个类,需要使用类名加上一对括号,如Animal()
,这将创建类的一个实例。实例化类时,会调用类的初始化函数__init__()
,并返回一个新的实例。实例化类的一个简单示例如下:
class Animal():def __init__(self, name):self.name = namedef eat(self):print(f"{self.name}正在吃东西.")dog = Animal("旺财") # 类的实例化
print(dog.name) # 输出: 旺财
dog.eat() # 输出:旺财正在吃东西.
在上述示例中,我们定义了一个Animal
类,并在其初始化函数中传入一个参数name
,在实例方法eat
中使用了传入的参数name
。然后,我们使用Animal
实例化一个对象dog
,并传入一个参数"旺财"
实例化类时,如果类没有定义__init__()
构造函数,则会使用默认构造函数。
拓展之self
参数:在Python中,self
是一个指向类实例本身的引用。类中的实例方法需要访问该实例,因此在定义实例方法时必须将self
作为第一个参数,以便在内部使用它。self
类似于C++中的this
指针,self
实际上是一个约定俗成的参数名称,它可以被替换为其它名称。
1.3 魔法函数
在Python中,有一些特殊的函数被称为魔法函数(magic method),也叫特殊方法。这些方法以双下划线__
开头和结尾,在类中重写这些方法可以实现一些特殊的行为,比如操作符重载、自定义对象转换等。(from chatGPT)
下面是类中几个常用的魔法方法:
__init__(self, ...)
:构造函数(也叫初始化函数),在创建对象时自动调用,用于初始化对象的属性__call__(self, ...)
:使对象可调用,当对象被当作函数如obj(...)
调用时,该方法会被触发__getitem__(self, key)
:获取特定索引值的元素,通过obj[key]
调用__iter__(self)
:该方法被调用时返回了一个实现了__next__()
方法的迭代器对象- 简单解释下迭代器是一个可以被迭代的对象,它能够在每次调用
__next__()
方法时返回其下一个值,直到所有值都被遍历完毕。 - 明确下列表、元组、字符串都是可迭代对象,但不是迭代器,它们本身不含有
__next__
方法,所以无法通过next()
函数来进行迭代,但是它们可以通过内置函数iter()
转换为迭代器 - 如果一个类实现了魔法方法
__iter__()
和__next__()
,那么它就可以被视为一个迭代器
- 简单解释下迭代器是一个可以被迭代的对象,它能够在每次调用
__next__(self)
:返回当前迭代器的下一个值,如果没有下一个值,则会抛出StopIteration
异常
简单的示例代码如下所示:
# __init__魔法方法
class Person:def __init__(self, name) -> None:self.name = nameperson = Person("Tom")
print(person.name) # 输出:Tom# __call__魔法方法
class Add:def __init__(self, num) -> None:self.num = numdef __call__(self, x):return self.num + xadd = Add(5)
print(add(3)) # 输出:8# __getitem__魔法方法
class MyList:def __init__(self, lst):self.lst = lstdef __getitem__(self, index):return self.lst[index]mylist = MyList([1, 2, 3])
print(mylist[1]) # 输出:2# __iter__和__next__魔法方法
class Reverse:def __init__(self, data) -> None:self.data = dataself.index = len(data)def __iter__(self):return selfdef __next__(self):if self.index == 0:raise StopIterationself.index = self.index - 1return self.data[self.index]rev = Reverse("hello")
for char in rev:print(char) # 输出:o l l e h
2.生成器
2.1 定义
Python中,生成器是一种特殊的迭代器(定义,本质上是迭代器),它可以通过生成器函数或生成器表达式来创建(创建方法)。生成器可以用于生成协程,通过send()
方法进行协程间的通信,实现异步编程和协作式多任务处理(用途)。
2.2 生成器函数
生成器函数是一种特殊的函数,它使用yield
语句来生成一个序列。当生成器函数被调用时,它返回一个生成器对象,可以通过该对象来迭代生成器中的元素。生成器可以用做懒加载,即只有在使用的生成器元素时才会生成该元素,而不是提前生成全部元素。
下面是一个简单的示例代码,演示如何使用生成器函数来创建一个生成器:
# 生成器函数
def my_generator(max):current = 0while current < max:yield currentcurrent += 1# 创建生成器
gen = my_generator(5)
for i in gen:print(i) # 输出:0 1 2 3 4
在这个示例代码中,首先调用my_generator
函数来创建一个生成器对象gen
。然后,我们使用for
循环来迭代gen
中的元素,并输出每个元素的值。在迭代过程中,Python会自动调用生成器对象的__next__
方法来获取下一个元素的值,直到生成器中的所有元素都被迭代完毕。
值得注意的是:当执行到yield
语句时,函数会暂停执行,并返回当前的值,同时将函数的状态保存下来。当下一次调用生成器对象的__next__
方法,函数会从上一次暂停的位置继续执行,直到遇到下一个yield
语句或函数结束。
2.3 生成器表达式
生成器表达式类似于列表推导式,但使用圆括号而不是方括号来表示。与列表推导式不同的是,生成器表达式并不立即生成一个列表,而是生成一个生成器对象。
下面是一个简单的示例代码,演示如何使用生成器表达式来创建一个生成器:
gen = (x for x in range(5))
for i in gen:print(i) # 输出:0 1 2 3 4
和生成器函数一样,我们也可以使用生成器表达式来实现更复杂的逻辑和条件判断,例如,筛选一个列表中的偶数元素,代码如下:
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
gen = (x for x in my_list if x % 2 == 0)
for i in gen:print(i) # 输出:2 4 6 8 10
3.迭代器和可迭代对象
3.1 迭代器
在Python中,迭代器是一种可以遍历容器中元素的对象,它可以逐个返回容器中的元素,并在遍历过程中记录当前位置。迭代器可以用于处理大量数据或无法一次加载到内存中的数据集(Dataset)。
在Python中,迭代器必须实现两个方法:__iter__
和__next__
。其中__iter__
方法返回迭代器对象本身,而__next__
方法返回容器中的下一个元素,如果容器中没有更多元素,则抛出StopIteration
异常。
下面是一个迭代器的示例:
class MyIterator:def __init__(self, data):self.index = 0self.data = datadef __iter__(self):return selfdef __next__(self):if self.index >= len(self.data):raise StopIterationresult = self.data[self.index]self.index += 1return resultmy_list = [1, 2, 3, 4]
my_iterator = MyIterator(my_list)for item in my_iterator:print(item) # 输出:1 2 3 4
在上面的代码中,MyIterator
类实现了__iter__
和__next__
方法,使得该类的实例my_iterator
成为一个迭代器对象。在for
循环中,我们使用my_iterator
来进行迭代,最终输出列表中的元素。注意,在迭代器中,我们呢需要手动实现__next__
方法,每次迭代都需要更新迭代器的状态。
3.2 可迭代对象
在Python中,可迭代对象是指实现了__iter__()
方法的对象,例如列表、元组、字符串等,它们可以被for循环遍历。
下面是可迭代对象的一个简单示例:
class MyIterable:def __init__(self, data):self.data = datadef __iter__(self):return iter(self.data)my_list = [1, 2, 3, 4]
my_iterable = MyIterable(my_list)for item in my_iterable:print(item) # 输出:1 2 3 4
上面的代码中,MyIterable
类实现了__iter__
方法,该方法通过调用Python内置函数iter()
返回了一个迭代器对象。在MyIterable
类的实例化中,我们将一个列表传递给了MyIterable
类,使得该类的实例my_iterable
成为一个可迭代对象。在for
循环中,我们使用my_iterable
来进行迭代,最终输出列表中的元素。
3.3 区别和联系
区别:可迭代对象只是一个具有__iter__()
方法的对象,而迭代器则是实现了__iter__()
方法和__next__()
方法的对象。可迭代对象可以被用于for循环中,但是如果我们需要使用next()
函数来逐一获取其元素,那么就需要将其转换成迭代器。
联系:可迭代对象可以通过实现__iter__()
方法来返回一个迭代器,迭代器可以用来遍历可迭代对象。另外,可迭代对象和迭代器都可以通过for循环来遍历元素。
下面是一个简单的示例,演示了如何将可迭代对象转换成迭代器并调用next()
方法进行遍历:
my_list = [1, 2, 3, 4, 5]
# 将列表对象转换为一个迭代对象
my_iter = iter(my_list)while True:try:# 获取下一个元素的值value = next(my_iter)print(value) # 输出:1 2 3 4 5except StopIteration:break
4.装饰器
4.1 定义和语法
Python的装饰器本质上是一个函数,它接受一个函数对象作为参数,并返回一个修改后的函数对象(参数和返回值均为函数对象)。装饰器通常使用@decorator
的语法糖来使用,它可以将装饰器应用于函数或类的定义之前,从而实现对其功能的增强或修改。(from chatGPT)
下面是一个简单的示例,演示了如何使用装饰器来打印函数的运行时间:
import timedef time_it(func):def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"Function {func.__name__} took {(end_time - start_time) * 1000 :.3f} ms.")return resultreturn wrapper@time_it # 等价于 my_func = time_it(my_func)
def my_func():time.sleep(1)my_func()
在上述代码中,我们定义了一个名为time_it
的装饰器函数,它接受一个函数对象作为参数,并返回一个修改后的函数对象。接着,我们使用@time_it
的语法糖将装饰器应用于函数my_func
的定义之前,这意味着函数my_func
将被传递给装饰器函数time_it
,并被修改为一个新的函数对象。当调用函数my_func
时,实际上调用的是被修改后的函数对象wrapper
,它会记录函数运行时间,并输出运行时间信息。
装饰器的语法结构如下:
@表达式def 被修饰的函数
其中,表达式需要返回一个函数对象,这个函数对象就是用来修饰函数的。以上面的代码为例,这里的表达式就是time_it
,被修饰的函数就是my_func
。上述装饰器也可以写成函数名 = 表达式(函数名)
的形式即my_func = time_it(my_func)
,可实现相同的功能。
4.2 类作为装饰器
上面实现了函数作为装饰器的用法,现在我们来看看类作为装饰器的几种形式。
4.2.1 形式1
使用class的__init__
作为装饰入口,传递my_func
;使用class的__call__
作为调用入口
示例代码如下:
import timeclass TimeIt():def __init__(self, func):self.func = funcdef __call__(self, *args, **kwargs):start_time = time.time()result = self.func(*args, **kwargs)end_time = time.time()print(f"Function {self.func.__name__} took {(end_time - start_time) * 1000 :.3f} ms.")return result@TimeIt # 相当于 my_func = TimeIt(my_func) 进行实例化
def my_func():time.sleep(1)my_func() # 相当于调用实例化对象的__call__魔术方法
这里使用类来实现装饰器,__init__方法
接受被装饰函数,__call__
方法作为装饰器的入口,接受被装饰函数的参数,计算其运行时间并打印。@TimeIt
相当于执行了my_func = TimeIt(my_func)
,将被装饰函数传递给装饰器的__init__
方法。
4.2.2 形式2
使用class的__call__
作为装饰入口,传递my_func
,返回wrapper;在warpper中实现统计耗时
import timeclass TimeIt():def __init__(self, name):self.name = namedef __call__(self, func):def wrapper():start_time = time.time()result = func()end_time = time.time()print(f"Function {self.name} took {(end_time - start_time) * 1000 :.3f} ms.")return resultreturn wrapper@TimeIt("test") # 相当于 my_func = TimeIt("test")(my_func)
def my_func():time.sleep(1)my_func()
这里实现是将被装饰的函数my_func
作为参数传递给了__call__
方法,因此,在执行@TimeIt("test")
时,实际上是创建了一个TimeIt
对象,并将"test"
作为参数传递给了该对象的__init__
初始化方法,然后将my_func
作为参数传递给了该对象的__call__
方法,最终执行了wrapper
函数,并返回了其返回值。
4.3 修饰类的成员函数
下面的示例演示了装饰器修饰类的成员函数:
import timedef time_it(func):def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"Function {func.__name__} took {(end_time - start_time) * 1000 :.3f} ms.")return resultreturn wrapperclass Tool():@time_it # 相当于my_func = time_it(my_func)def my_func():time.sleep(1)Tool.my_func()
上述代码中,@time_it
是一个装饰器,用来修饰Tool
类的成员函数my_func
。通过在my_func
前加上@time_it
,实现了对my_func
的装饰,使得my_func
在被调用时,会先调用time_it
函数统计函数运行时间。调用Tool.my_func()
时,实际上是调用了wrapper
函数,并将其结果返回。
4.4 常见装饰器
在Python中有一些常见的内置装饰器,如@classmethod
、@staticmethod
、@property
,下面我们分别介绍下:(from chatGPT)
@classmethod
装饰器用于将一个函数定义为类方法,类方法可以在不创建实例的情况下被类本身调用,常用于在类级别上操作或返回类的某些属性或方法。使用@classmethod
修饰的函数的第一个参数必须是cls
,代表调用该方法的类本身。@staticmethod
装饰器用于将一个函数定义为静态方法,静态方法和类方法类似,但是在静态方法中不需要传递类或实例参数,使用@staticmethod
修饰的函数没有特殊的参数要求,因为它们与类或实例无关,所以可以在不实例化类的情况下直接调用。@property
装饰器用于将一个函数定义为属性,使得该函数可以像类属性一样被访问,但是每次访问该属性时都会动态计算。是使用@property
修饰的函数必须有一个返回值,该返回值就是该属性的值。
下面是一个简单的示例,用来说明这几种内置装饰器的功能:
class Myclass:def __init__(self, value):self._value = value@classmethoddef from_string(cls, value_string):value = int(value_string)return cls(value) # 等价于 Myclass(value)@staticmethoddef say_hello():print("Hello!")@propertydef value(self):return self._value@value.setterdef value(self, new_value):self._value = new_valueinstance = Myclass.from_string("123")
Myclass.say_hello() # 输出:Hello!
instance.say_hello() # 输出:Hello!
print(instance.value) # 输出:123
instance.value = 456
print(instance.value) # 输出:456
在这个示例中,我们首先使用类方法from_string
来创建一个MyClass
实例,然后调用静态方法say_hello
,最后输出value
属性的值,并通过属性赋值方法来修改它的值。对于该示例主要有以下几点说明:
- 这个示例定义了一个名为
MyClass
的类,它有一个构造函数__init__
,一个类方法from_string
,一个静态方法sat_hello
和一个属性value
from_string
方法是一个类方法,它用于根据传入的字符串参数创建一个MyClass
的实例。在这个方法内部,我们首先将字符串转换为整数类型,然后使用cls
参数来创建一个新的类实例。say_hello
方法是一个静态方法,它不需要访问任何实例属性或者类属性,因此它可以作为类方法而不是实例方法来定义。我们可以在不实例化MyClass
的情况下,直接调用这个静态方法。value
是一个实例属性,我们可以通过@property
装饰器将其变成只读属性,即只能通过instance.value
来读取,而不能通过instance.value = new_value
来修改。如果我们希望value
属性是可写的,那么可以再定义一个带有@value.setter
装饰器的方法,用于处理新值的赋值操作。
4.5 基于装饰器的分配机制
基于装饰器的分配机制,是指在Python中,装饰器可以用于为类或函数动态地添加额外地属性或方法。通过装饰器,我们可以在不修改原始类或函数定义的情况下,为它们添加新的功能。在Python中,这种机制被称为基于装饰器的分配机制(Decorator-Based Dispatching)。
例如,我们可以使用装饰器为一个函数添加日志功能,如下所示:
def log(func):def wrapper(*args, **kwargs):print("Calling function:", func.__name__)resutlt = func(*args, **kwargs)print("Function result:", resutlt)return resutltreturn wrapper@log
def add(a, b):return a + badd(1, 2) # 输出:Calling function: add Function result: 3
在这个例子中,log
装饰接收一个函数作为参数,返回一个新的函数wrapper
,当我们调用add
函数时,实际上是调用了wrapper
函数,从而实现了日志功能的添加。
通过装饰器,我们可以轻松地为类和函数添加新的功能,而不需要修改它们地定义。这样,我们可以保持代码的简洁性和可读性,同时又能够满足不同的需求。
5.作业
5.1 作业7
内容:自定义类Fib(count),通过迭代,每次吐出一个斐波拉契数列数列值,最多count次
可以利用迭代器,在类Fib
重写__iter__
和__next__
方法,以实现迭代输出斐波那契数列的功能,示例代码如下:
class Fib:def __init__(self, count):self.count = countself.current = 0self.next = 1self.index = 0# 返回迭代器对象def __iter__(self):return self# 获取下一个迭代器元素def __next__(self):if self.index >= self.count:raise StopIterationresult = self.currentself.current, self.next = self.next, self.current + self.nextself.index += 1return resultfor i, number in enumerate(Fib(10)):print(f"第 {i+1:<2} 个数是 {number:<2}") # :<2表示输出宽度为2,左对齐
输出结果如下:
拓展之enumerate
enumerate
是Python中的一个内置函数,用于枚举一个可迭代对象的元素并返回一个由元素下标和元素本身组成的二元组。其语法格式如下:
enumerate(iterable, start=0)
其中,iterable
表示要枚举的可迭代对象,start
表示起始下标,默认为0。返回的是一个迭代器对象。enumerate
函数在循环中非常有用,可以同时获得下标和元素的值。
下面是一个简单示例:
fruits = ['apple', 'banana', 'orange']
for i, fruit in enumerate(fruits):print(i, fruit) # 输出:0 apple 1 banana 2 orange
5.2 作业8
内容:自定义类装饰器,修饰作业7的类,并修改这个函数的返回值+5
示例代码如下:
class Hook:def __init__(self, name):self.name = namedef __call__(self, func):def warpper(*args, **kwargs):result = func(*args, **kwargs)return result + 5return warpperclass Fib:def __init__(self, count):self.count = countself.current = 0self.next = 1self.index = 0# 返回迭代器对象def __iter__(self):return self# 获取下一个迭代器元素@Hook("FibTimer") # __next__ = Hook(__next__)def __next__(self):if self.index >= self.count:raise StopIterationresult = self.currentself.current, self.next = self.next, self.current + self.nextself.index += 1return resultfor i, number in enumerate(Fib(10)):print(f"第 {i+1:<2} 个数是 {number:<2}") # :<2表示输出宽度为2,左对齐
输出结果如下:
总结
本次课程学习的内容挺多的,首先学习了类的基本概念和魔法函数,其次学习了Python中的三大神器,分别是生成器、迭代器和装饰器,了解了构建生成器的两种方式:生成器函数和生成器表达式。了解了迭代器在类中的使用(通过
__iter__
和__next__
),同时还了解了装饰器的相关语法(包括函数作为装饰器和类作为装饰器)以及python中常见的内置装饰器。