이 글은 효율적이고 우아한 Python 코드를 작성하는 방법을 주로 소개하고 공유합니다. 필요한 친구들은 참고해도 됩니다.
이 글의 일부는 "Effective Python" & "Python3 Cookbook"이라는 책에서 발췌되었지만, 수정 및 추가된 내용은 작성자가 직접 이해하고 적용한 모범 사례입니다.
전체 텍스트는 약 9956 단어이며, 읽는 데 24분이 걸릴 수 있습니다.
Pythonic 목록 자르기
list[start:end:step]
list[start:end:step]
如果从列表开头开始切割,那么忽略 start 位的 0,例如list[:4]
如果一直切到列表尾部,则忽略 end 位的 0,例如list[3:]
切割列表时,即便 start 或者 end 索引跨界也不会有问题
列表切片不会改变原列表。索引都留空时,会生成一份原列表的拷贝
列表推导式
使用列表推导式来取代map
和filter
不要使用含有两个以上表达式的列表推导式
数据多时,列表推导式可能会消耗大量内存,此时建议使用生成器表达式
迭代
需要获取 index 时使用enumerate
enumerate
可以接受第二个参数,作为迭代时加在index
上的数值
用zip
同时遍历两个迭代器
zip
遍历时返回一个元组
关于for
和while
循环后的else
块
循环正常结束之后会调用else
内的代码
循环里通过break
跳出循环,则不会执行else
要遍历的序列为空时,立即执行else
反向迭代
对于普通的序列(列表),我们可以通过内置的reversed()
函数进行反向迭代:
除此以外,还可以通过实现类里的__reversed__
方法,将类进行反向迭代:
try/except/else/finally
如果try
内没有发生异常,则调用else
内的代码
else
会在finally
之前运行
最终一定会执行finally
,可以在其中进行清理工作
函数使用装饰器
装饰器用于在不改变原函数代码的情况下修改已存在的函数。常见场景是增加一句调试,或者为已有的函数增加log
监控
举个栗子:
除此以外,还可以编写接收参数的装饰器,其实就是在原本的装饰器上的外层又嵌套了一个函数:
但是像上面那样使用装饰器的话有一个问题:
也就是说原函数已经被装饰器里的new_fun
函数替代掉了。调用经过装饰的函数,相当于调用一个新函数。查看原函数的参数、注释、甚至函数名的时候,只能看到装饰器的相关信息。为了解决这个问题,我们可以使用
Python 自带的functools.wraps
方法。
functools.wraps
list [:4]
목록 끝까지 잘라내는 경우 list[3:]와 같은 끝 비트의 0을 무시하세요. code>
map
및 filter
🎜🎜🎜🎜두 개 이상의 표현식이 포함된 목록 이해를 사용하지 마세요🎜🎜🎜🎜데이터가 많은 경우 리스트 컴프리헨션은 메모리를 많이 소모할 수 있습니다. 이 경우 권장됩니다. 생성기 표현식을 사용하려면🎜🎜🎜🎜Iteration🎜🎜 enumerate🎜🎜enumerate
는 반복 중에 index
에 추가된 값으로 두 번째 매개변수를 허용할 수 있습니다🎜🎜🎜🎜zip
을 사용하여 두 개의 반복기를 순회하세요. 동시에🎜🎜🎜🎜zip
순회할 때 튜플을 반환합니다🎜🎜🎜🎜for
및 while
루프 뒤의 else
블록 🎜🎜루프가 정상적으로 종료된 후 else의 코드
는 루프break
를 통해 🎜🎜호출됩니다. 루프를 벗어나면 else
는 실행되지 않습니다.🎜🎜순회할 시퀀스가 비어 있으면 else
🎜🎜🎜🎜역방향 반복🎜🎜일반 시퀀스(목록)의 경우 내장된 reversed()
함수를 사용하여 역방향 반복을 수행할 수 있습니다. 🎜🎜🎜🎜또한 __reversed__
를 구현하여 클래스를 역반복할 수도 있습니다. > 클래스의 메소드: 🎜🎜🎜🎜 try/excess/else/finally
🎜🎜if try code> 내에서 예외가 발생하지 않으면 <code>else
내의 코드가 호출됩니다🎜🎜else code>는 <code>finally
🎜🎜결국 finally
를 실행하며 여기서 정리 작업을 수행할 수 있습니다🎜🎜함수는 데코레이터를 사용합니다🎜🎜데코레이터는 코드를 변경하지 않고 기존 함수를 수정하는 데 사용됩니다. 원래 함수 코드. 일반적인 시나리오는 디버깅 문장을 추가하거나 기존 함수에 대한 log
모니터링을 추가하는 것입니다🎜🎜예: 🎜🎜🎜🎜또한 매개변수를 받는 데코레이터를 작성할 수도 있습니다. 실제로 원래 데코레이터의 외부 레이어에 함수가 중첩되어 있습니다. :🎜🎜🎜🎜하지만 위와 같이 데코레이터를 사용하면 질문이 있습니다: 🎜🎜🎜🎜즉, , 원래 함수는 데코레이터의 new_fun
함수로 대체되었습니다. 장식된 함수를 호출하는 것은 새 함수를 호출하는 것과 같습니다. 원래 함수의 매개변수, 설명, 함수명까지 보면 데코레이터에 관련된 정보만 볼 수 있습니다. 이 문제를 해결하기 위해 Python과 함께 제공되는 functools.wraps
메서드를 사용할 수 있습니다. 🎜🎜functools.wraps
는 매우 해킹적인 방법입니다. 데코레이터 내부에서 반환되는 함수에 대한 데코레이터로 사용할 수 있습니다. 즉, 데코레이터 중의 데코레이터로서 원래 함수를 매개변수로 취하는 역할을 하며, 원래 함수의 다양한 정보를 유지해 나중에 데코레이팅된 원래 함수의 정보를 볼 때 그대로 유지되도록 하는 기능이다. 원래 기능과 동일합니다. 🎜🎜🎜🎜게다가 때로는 데코레이터가 두 가지 이상의 작업을 수행할 수도 있으며, 이 경우 이벤트는 추가 기능으로 분리되어야 합니다. 하지만 데코레이터에만 관련될 수 있으므로 이때 데코레이터 클래스를 생성할 수 있습니다. 원리는 매우 간단합니다. 클래스를 함수처럼 호출할 수 있도록 클래스에 __call__
메서드를 작성하는 것이 핵심입니다. __call__
方法,使类能够像函数一样的调用。
使用生成器
考虑使用生成器来改写直接返回列表的函数
用这种方法有几个小问题:
每次获取到符合条件的结果,都要调用append
方法。但实际上我们的关注点根本不在这个方法,它只是我们达成目的的手段,实际上只需要index
就好了
返回的result
可以继续优化
数据都存在result
里面,如果数据量很大的话,会比较占用内存
因此,使用生成器generator
会更好。生成器是使用yield
表达式的函数,调用生成器时,它不会真的执行,而是返回一个迭代器,每次在迭代器上调用内置的next
函数时,迭代器会把生成器推进到下一个yield
表达式:
获取到一个生成器以后,可以正常的遍历它:
如果你还是需要一个列表,那么可以将函数的调用结果作为参数,再调用list
方法
可迭代对象
需要注意的是,普通的迭代器只能迭代一轮,一轮之后重复调用是无效的。解决这种问题的方法是,你可以定义一个可迭代的容器类:
这样的话,将类的实例迭代重复多少次都没问题:
但要注意的是,仅仅是实现__iter__
方法的迭代器,只能通过for
循环来迭代;想要通过next
方法迭代的话则需要使用iter
方法:
使用位置参数
有时候,方法接收的参数数目可能不一定,比如定义一个求和的方法,至少要接收两个参数:
对于这种接收参数数目不一定,而且不在乎参数传入顺序的函数,则应该利用位置参数*args
:
但要注意的是,不定长度的参数args
在传递给函数时,需要先转换成元组tuple
。这意味着,如果你将一个生成器作为参数带入到函数中,生成器将会先遍历一遍,转换为元组。这可能会消耗大量内存:
使用关键字参数
关键字参数可提高代码可读性
可以通过关键字参数给函数提供默认值
便于扩充函数参数
定义只能使用关键字参数的函数
普通的方式,在调用时不会强制要求使用关键字参数
使用 Python3 中强制关键字参数的方式
使用 Python2 中强制关键字参数的方式
关于参数的默认值
算是老生常谈了:函数的默认值只会在程序加载模块并读取到该函数的定义时设置一次
也就是说,如果给某参数赋予动态的值(
比如[]
或者{}
🎜고려하세요 생성기를 사용하여 목록을 직접 반환하는 함수를 다시 작성하세요🎜🎜🎜🎜이 방법에는 몇 가지 작은 문제가 있습니다. 🎜🎜조건을 충족하는 결과를 얻을 때마다
append
메서드를 호출해야 합니다. 그러나 사실 우리의 초점은 이 방법에 전혀 있지 않습니다. 실제로는 index
만 필요합니다. > 계속해서 최적화할 수 있습니다. 🎜🎜데이터는 결과
에 저장됩니다. 데이터 양이 많으면 더 많은 메모리를 차지하게 됩니다.🎜🎜생성기를 사용하는 것이 좋습니다. 생성기. 생성기는 yield
표현식을 사용하는 함수입니다. 생성기가 호출되면 실제로 실행되지 않지만 내장된 next 함수를 사용하면 반복자는 생성기를 다음 <code>yield
표현식으로 진행합니다: 🎜🎜🎜🎜생성기를 얻은 후 정상적으로 탐색할 수 있습니다: 🎜🎜🎜🎜 그래도 목록이 필요한 경우 함수 호출 결과를 매개변수로 사용한 다음 list
메서드를 호출할 수 있습니다 🎜 🎜🎜🎜Iterable 개체🎜🎜주의해야 할 점은 , 일반 반복자는 한 라운드 동안만 반복할 수 있으며 한 라운드 이후의 반복 호출은 유효하지 않습니다. 이 문제를 해결하는 방법은 반복 가능한 컨테이너 클래스를 정의할 수 있다는 것입니다: 🎜🎜🎜🎜이 경우 클래스 인스턴스를 몇 번 반복하는지는 중요하지 않습니다. 🎜🎜🎜🎜그러나 이는 __iter__
메서드를 구현하는 반복자일 뿐이라는 점에 유의해야 합니다. 반복하려면 for
루프를 통해서만 전달할 수 있습니다. next
메서드를 통해 반복하려면 iter
메서드를 사용해야 합니다. 🎜 🎜🎜🎜위치 매개변수를 사용하세요🎜🎜때때로 합산 방법 정의와 같이 메소드에서 수신한 매개변수는 확실하지 않을 수 있으며 최소한 두 개의 매개변수를 수신해야 합니다. 🎜🎜🎜🎜 특정 개수의 매개변수가 없고 매개변수가 전달되는 순서를 신경 쓰지 않는 이런 종류의 함수의 경우 위치 매개변수 * args
를 사용해야 합니다: 🎜🎜🎜🎜 하지만 가변 길이 매개변수 args
를 먼저 함수에 전달해야 한다는 점에 유의하세요. 튜플 tuple
로 변환하세요. 즉, 생성기를 함수의 매개변수로 전달하면 생성기가 먼저 순회되어 튜플로 변환됩니다. 이는 많은 메모리를 소모할 수 있습니다: 🎜🎜🎜🎜사용 키워드 매개변수🎜🎜키워드 매개변수는 코드 가독성을 향상시킬 수 있습니다🎜🎜키워드 매개변수를 통해 함수에 기본값을 제공할 수 있습니다🎜🎜함수 매개변수 확장이 쉽습니다🎜🎜키워드 매개변수만 사용할 수 있는 함수 정의🎜 🎜일반적인 방법으로는 호출 시 키워드 매개변수가 필요하지 않습니다.🎜🎜 🎜🎜Python3🎜🎜🎜🎜Python2🎜🎜🎜🎜매개변수의 기본값에 대해🎜🎜 진부한 표현입니다.함수의 기본값은 프로그램이 모듈을 로드하고 읽을 때만 로드됩니다. 함수가 정의될 때 한 번 설정됩니다.🎜🎜즉, 동적 값이 매개변수에 할당된 경우(🎜🎜예: []
또는 {}), 나중에 함수를 호출할 때 해당 매개변수에 다른 매개변수가 할당된 경우 나중에 이 함수를 다시 호출하면 이전에 정의된 기본값이 변경되어 마지막 호출에서 할당된 값이 됩니다. 🎜<p><img alt="" src="https://img.php.cn/upload/article/000/000/007/c86d9b3433dbdd9243a7ea7c863f9498-28.png"></p>
<p> 따라서 기본 매개변수로 <code>None
을 사용하고, 함수 내에서 판단 후 값을 할당하는 것이 더 좋습니다: None
作为默认参数,在函数内进行判断之后赋值:
类__slots__
默认情况下,Python 用一个字典来保存一个对象的实例属性。这使得我们可以在运行的时候动态的给类的实例添加新的属性:
然而这个字典浪费了多余的空间 -— 很多时候我们不会创建那么多的属性。因此通过__slots__
可以告诉 Python
不要使用字典而是固定集合来分配空间。
__call__
通过定义类中的__call__
方法,可以使该类的实例能够像普通函数一样调用。
通过这种方式实现的好处是,可以通过类的属性来保存状态,而不必创建一个闭包或者全局变量。
@classmethod
& @staticmethod
@classmethod
和@staticmethod
很像,但他们的使用场景并不一样。
类内部普通的方法,都是以self
作为第一个参数,代表着通过实例调用时,将实例的作用域传入方法内;
@classmethod
以cls
作为第一个参数,代表将类本身的作用域传入。无论通过类来调用,还是通过类的实例调用,默认传入的第一个参数都将是类本身
@staticmethod
不需要传入默认参数,类似于一个普通的函数
来通过实例了解它们的使用场景:
假设我们需要创建一个名为Date
的类,用于储存 年/月/日 三个数据
上述代码创建了Date
类,该类会在初始化时设置day/month/year
属性,并且通过property
设置了一个getter
,可以在实例化之后,通过time
获取存储的时间:
但如果我们想改变属性传入的方式呢?毕竟,在初始化时就要传入年/月/日三个属性还是很烦人的。能否找到一个方法,在不改变现有接口和方法的情况下,可以通过传入2016-11-09
这样的字符串来创建一个Date
实例?
你可能会想到这样的方法:
但不够好:
在类外额外多写了一个方法,每次还得格式化以后获取参数
这个方法也只跟Date
类有关
没有解决传入参数过多的问题
此时就可以利用@classmethod
,在类的内部新建一个格式化字符串,并返回类的实例的方法:
这样,我们就可以通过Date
类来调用from_string
方法创建实例,并且不侵略、修改旧的实例化方式:
好处:
在@classmethod
内,可以通过cls
参数,获取到跟外部调用类时一样的便利
可以在其中进一步封装该方法,提高复用性
更加符合面向对象的编程方式
而@staticmethod
,因为其本身类似于普通的函数,所以可以把和这个类相关的 helper
方法作为@staticmethod
,放在类里,然后直接通过类来调用这个方法。
将与日期相关的辅助类函数作为@staticmethod
方法放在Date
类内后,可以通过类来调用这些方法:
创建上下文管理器
上下文管理器,通俗的介绍就是:在代码块执行前,先进行准备工作;在代码块执行完成后,做收尾的处理工作。with
__slots__
🎜🎜기본적으로 Python은 사전을 사용하여 인스턴스 속성을 저장합니다. 객체. 이를 통해 런타임 시 클래스 인스턴스에 새 속성을 동적으로 추가할 수 있습니다. 🎜🎜🎜🎜그러나 이 사전은 추가 공간을 낭비합니다. 많은 경우 우리는 그렇게 많은 속성을 생성하지 않을 것입니다. 따라서 __slots__
는 Python🎜🎜에 사전을 사용하지 않고 고정 컬렉션을 사용하여 공간을 할당하도록 지시할 수 있습니다. 🎜🎜🎜🎜__call__
🎜 🎜클래스에 __call__
메서드를 정의하면 클래스의 인스턴스를 일반 함수처럼 호출할 수 있습니다. 🎜🎜🎜🎜이런 방식으로 구현할 때의 장점은, 클로저나 전역 변수를 만들지 않고도 클래스 속성을 통해 상태를 저장할 수 있습니다. 🎜🎜@classmethod
& @staticmethod
🎜🎜@classmethod
와 @staticmethod
는 매우 유사하지만 사용 시나리오 동일하지 않습니다. 🎜🎜클래스 내의 일반 메서드는 모두 첫 번째 매개 변수로 self
를 사용합니다. 즉, 인스턴스를 통해 호출되면 인스턴스 범위가 메서드에 전달됩니다. 🎜🎜@classmethod는 <code>cls
를 첫 번째 매개변수로 사용합니다. 이는 클래스 자체의 범위를 전달한다는 의미입니다. 클래스를 통해 호출되는지, 클래스의 인스턴스를 통해 호출되는지에 관계없이 기본적으로 전달되는 첫 번째 매개변수는 클래스 자체가 됩니다.🎜🎜@staticmethod
는 다음과 유사하게 기본 매개변수를 전달할 필요가 없습니다. 일반적인 함수 🎜🎜예제를 통해 사용 시나리오를 이해해 보겠습니다. 🎜🎜연/월/일의 세 가지 데이터를 저장하기 위해 Date
라는 클래스를 만들어야 한다고 가정합니다🎜🎜🎜🎜위 코드는 Date
클래스를 생성합니다. 일/월/년
속성은 초기화 중에 설정되고 getter
는 속성
을 통해 설정됩니다. 이 속성은 인스턴스화 후에 에 전달될 수 있습니다. time
저장된 시간 가져오기: 🎜🎜🎜 🎜하지만 속성이 전달되는 방식을 변경하려면 어떻게 해야 할까요? 결국 초기화 시 연/월/일 3가지 속성을 전달해야 하는 것이 귀찮습니다. 기존 인터페이스와 메서드를 변경하지 않고 2016-11-09
와 같은 문자열을 전달하여 Date
인스턴스를 생성하는 방법을 찾을 수 있습니까? 🎜🎜이 방법을 생각해 보세요: 🎜🎜🎜 🎜 하지만 충분하지 않습니다. 🎜🎜클래스 외부에 추가 메서드를 작성하고 매개변수를 가져오려면 매번 형식을 지정해야 합니다.🎜🎜이 메서드는 Date
클래스에만 관련됩니다🎜🎜그렇습니다 들어오는 매개변수를 해결하지 마세요. 문제가 너무 많습니다🎜🎜이때 @classmethod
를 사용하여 클래스 내부에 새 형식 문자열을 만들고 클래스 인스턴스의 메서드를 반환할 수 있습니다. 🎜🎜🎜🎜이런 식으로 Date
클래스 >from_string 메소드는 이전 인스턴스화 메소드를 침해하거나 수정하지 않고 인스턴스를 생성합니다: 🎜🎜🎜🎜이점: 🎜🎜@classmethod
에서 cls
매개변수를 사용하여 동일한 결과를 얻을 수 있습니다. 외부 클래스를 호출할 때처럼 편리합니다. 🎜🎜재사용성을 높이기 위해 메소드를 추가로 캡슐화할 수 있습니다🎜🎜객체 지향 프로그래밍에 더 가깝습니다🎜🎜그리고 @staticmethod
는 다음과 유사합니다. 일반 함수는 이 클래스 관련 도우미와 결합될 수 있습니다🎜🎜 메서드는 @staticmethod
로 클래스에 배치되며 클래스를 통해 직접 메서드가 호출됩니다. 🎜🎜🎜🎜 날짜 관련 보조 클래스 함수를 After로 사용하세요 @staticmethod
메서드는 Date
클래스에 배치되며 다음 메서드는 클래스를 통해 호출할 수 있습니다. 🎜🎜🎜🎜컨텍스트 관리자 만들기🎜🎜컨텍스트 관리자, 인기 있는 소개는 다음과 같습니다. 코드 블록이 실행되기 전 준비 작업은 다음과 같습니다. done first; in 코드 블록이 실행된 후 최종 처리를 수행합니다. with
문은 컨텍스트 관리자와 함께 나타나는 경우가 많습니다. 기본 시나리오는 다음과 같습니다. 🎜
with
문을 통해 코드는 파일 열기 작업을 완료하고 호출이 종료되거나 읽기 중 예외, 즉 파일을 읽고 쓴 후의 처리가 발생하면 자동으로 파일을 닫습니다. 완료되었습니다. 컨텍스트 관리자를 통과하지 못한 경우 코드는 다음과 같습니다. with
语句,代码完成了文件打开操作,并在调用结束,或者读取发生异常时自动关闭文件,即完成了文件读写之后的处理工作。如果不通过上下文管理器的话,则会是这样的代码:
比较繁琐吧?所以说使用上下文管理器的好处就是,通过调用我们预先设置好的回调,自动帮我们处理代码块开始执行和执行完毕时的工作。而通过自定义类的__enter__
和__exit__
方法,我们可以自定义一个上下文管理器。
然后可以以这样的方式进行调用:
在调用的时候:
with
语句先暂存了ReadFile
类的__exit__
方法
然后调用ReadFile
类的__enter__
方法
__enter__
方法打开文件,并将结果返回给with
语句
上一步的结果被传递给file_read
参数
在with
语句内对file_read
参数进行操作,读取每一行
读取完成之后,with
语句调用之前暂存的__exit__
方法
__exit__
方法关闭了文件
要注意的是,在__exit__
方法内,我们关闭了文件,但最后返回True
,所以错误不会被with
语句抛出。否则with
__enter__
및 __exit__
메서드를 사용자 정의하여 컨텍스트 관리자를 사용자 정의할 수 있습니다. 🎜🎜🎜🎜그런 다음 다음과 같이 호출할 수 있습니다. 🎜🎜🎜🎜전화할 때: 🎜🎜 with
문은 먼저 ReadFile
클래스 🎜🎜의 __exit__
메서드를 임시로 저장한 다음 ReadFile의 <code>__enter__를 호출합니다. code> 클래스.code> 메소드🎜🎜<code>__enter__
메소드는 파일을 열고 결과를 with
문에 반환합니다.🎜🎜이전 단계의 결과가 file_read
매개변수🎜 🎜with
문 내의 file_read
매개변수에 대한 작업, 각 줄 읽기 🎜🎜읽기가 완료된 후 with 문은 이전에 저장된 <code>__exit__
메서드를 호출합니다. 🎜🎜__exit__
메서드는 파일을 닫습니다.🎜🎜__exit__
메서드 내에서 주의해야 합니다. , 파일을 닫지만 마지막에 True
를 반환하므로 with
문에서 오류가 발생하지 않습니다. 그렇지 않으면 with
문에서 해당 오류가 발생합니다. 🎜위 내용은 효율적인 Python 코드를 작성하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!