두 제안 사이클을 쓰기 파이썬 | 거위 전투 공장

저자 : 텐센트 기술 프로젝트
링크 : HTTPS : //zhuanlan.zhihu.com/p/68128557의
출처는 : 거의 아는
저자가 저작권을 소유 한. 상업 무단 전재 저자 권한은 비상업적 무단 전재 소스를 표시하시기 바랍니다 문의하시기 바랍니다.

루프는 일반적인 프로그램의 제어 구조이다. 우리는 종종 인간의 기계에 비해 가장 큰 장점 중 하나는, 즉, 기계가 뭔가를 쉬지 않고 반복 될 수 있다고하지만, 사람들은 아니다. **은 "루프"**는 핵심 개념은 기계 반복 작업을 할 수 있습니다.

문법주기, 파이썬 성능 전통에서 전통 아니라고. 그것은 일반적인 포기하면서 for (init; condition; incrment)세 단계의 구조를하지만, 선택 forwhile두 고전 키워드주기를 표현. 대부분의 경우, 우리의 수요주기가 사용할 수 있습니다 for <item> in <iterable>충족하기 위해, while <condition>어떤 대비가 더 적은.

비록 순환 구문은 매우 간단하지만 정말 쓰기 쉬운 일이 아니다. 이 문서에서는, 우리는 루프 코드의 "전형적인"무엇 모색 할 것, 방법을 쓰기.


은 "정통"사이클은 무엇입니까?

"정통"는 종종 뭔가를 할 사람을 설명하는 데 사용되는 단어입니다,이 지역의 전통과 일치, 아주 잘. 비 유적으로, 친구의 모임에 가서, 같은 테이블 광둥, 다른 오프닝이 있었다, 모든 문장은 표준 베이징 악센트, 사운드의 완벽한 아이입니다. 그럼 당신은 그녀에게 말할 수있다 : "당신의 베이징 진정한 말 본격적인를 ."

은 "정통"는 종종 악센트를 설명하는 단어이기 때문에, 요리의 종류는 진짜 맛하여 루프 코드의 "전형적인"그것이 무슨 뜻인가? 내가 고전적인 예를 들어 설명을하자.

당신은 파이썬 월 학습 사람을 묻는다면 : " 목록을 순회하면서 어떻게 현재의 인덱스를 얻으려면? ." 그는이 코드를 넘겨 수 있습니다 :

index = 0
for name in names:
    print(index, name)
    index += 1

루프는 위의 사실이지만, 그되지 않지만 "진짜." 파이썬 코드는 다음과 같이 할 말을 사람들의 개발에 3 년의 경험을 가지고 :

for i, name in enumerate(names):
    print(i, name)

enumerate()파이썬는 파라미터로서 "반복 될 수있다"객체를 수신하고, 그 후 연속적으로 생성 리턴하는 내장 함수 (当前下标, 当前元素)새로운 객체가 반복 할 수있다. 이 시나리오는 가장 적절한 사용합니다.

따라서, 위의 예에서 우리는 생각보다 루프 코드의 두 번째 단락의 첫 번째 단락 "진짜." 그것은보다 직관적 인 코드를 사용하기 때문에, 스마트 작업을 완료했다.

) (대표 프로그래밍 아이디어를 열거

그러나 코드는 특정주기 정통 여부, 그리고 단지 또는 표준으로 내장 방법에 대한 지식없이 결정된다. 우리는 위의 예에서 더 깊은 무언가를 발굴 할 수 있습니다.

당신이 볼 수 있듯이, 파이썬의 for순환 아니라 for <item> in <iterable>- 구조의이 종류 및 상반기 구조 항목에 할당 - 너무 많은 트릭 재생할 수없는가. 따라서 하반기 반복자는 우리가 사물의 큰 소란을 할 수있는 유일한 일이다. 그리고하기 위해 enumerate()"수정 기능"* *의 대표 기능, 단지 새로운 아이디어를 제공합니다 반복 가능 객체를 수정하여 루프 자체를 최적화 할 수 있습니다.

이것은 내 첫 번째 제안에 저를 제공합니다.


추천 1 : 기능을 사용하여 루프 반복 객체를 최적화하기 위해 수정해야

반복 가능 객체 수정 기능 처리를 사용하여, 다양한 측면에서 루프 코드에 영향을 줄 수 있습니다. 그리고 너무 많이 차지하지 않고이 방법을 설명하는 적절한 예를 찾기 위해, 내장 된 모듈 itertools은 완벽한 예입니다.

간단히, itertools 기능은 다수의 반복 객체 지향 될 수있어서 공구의 세트이다. 나는 기사의 이전 시리즈에 있었다 "출입구 용기" 그 안에 언급했다.

당신이 itertools을 배우고 싶은 경우에, 공식 파이썬 문서는 당신의 선택은 관련 정보가 매우 상세한 모듈을가한다. 그러나이 문서에서, 초점은 약간 다른 공식 문서가 될 것입니다. 이 코드의 순환을 개선하는 것이 얼마나 내가 자세히 설명하는 몇 가지 일반적인 코드 시나리오를 통과 할 수 있습니다.

1. 중첩 루프 제품을 평평

우리 모두는 *이 * "좋은 이상의 평면 중첩 된 코드를"알고 있지만. 그러나 때때로 특정에 대한 수요가, 그것은 중첩 루프 작업을 쓰고있어 보인다. 예를 들어, 통로를 다음

def find_twelve(num_list1, num_list2, num_list3):
    """从 3 个数字列表中,寻找是否存在和为 12 的 3 个数
    """
    for num1 in num_list1:
        for num2 in num_list2:
            for num3 in num_list3:
                if num1 + num2 + num3 == 12:
                    return num1, num2, num3

중첩 루프 코드이 필요 우리가 사용할 수있는, 다층 다수의 오브젝트를 통과하는 제품 () 를 최적화하는 기능. product()복수의 오브젝트를 수신하는 반복 될 다음 연속적 직교 그들의 제품에 기초하여 결과를 생성 할 수있다.

from itertools import product


def find_twelve_v2(num_list1, num_list2, num_list3):
    for num1, num2, num3 in product(num_list1, num_list2, num_list3):
        if num1 + num2 + num3 == 12:
            return num1, num2, num3

사용 이전 코드에 비해 product()작업을 완료하는 데 한 사이클에 사용되는 기능을 코드가 더 세련됩니다.

2. 인터레이스 islice 달성 내부 루프

외부 데이터 파일은 콘텐츠 형식이있는 레딧 게시물 제목을 가지고 포함 :

python-guide: Python best practices guidebook, written for humans.
---
Python 2 Death Clock
---
Run any Python Script with an Alexa Voice Command
---
<... ...>

아마 미학이 문서 사이의 매 2 개 타이틀 들어있다 "---"세퍼레이터. 이제, 우리는 제목의 목록에있는 모든 문서를 얻을 필요가 있기 (위해) 때문에,이 파일의 내용을 통과, 이러한 의미 구분을 생략해야합니다.

참고 이전에 enumerate()기능을 이해하고, 우리의 현재 사이클 수에 따라 사이클 기간에 추가 할 수있는 if이 작업을 수행하는 판단 :

def parse_titles(filename):
    """从隔行数据文件中读取 reddit 主题名称
    """
    with open(filename, 'r') as fp:
        for i, line in enumerate(fp):
            # 跳过无意义的 '---' 分隔符
            if i % 2 == 0:
                yield line.strip()

그러나에 itertools를 사용하는 경우, 사이클 처리 인터레이스 드의 타입에 대한 수요 islice () 함수는 반복 코드가 더 단순하고 직접적인 수 개체에게 순환 수정된다.

islice(seq, start, end, step)함수와 어레이 슬라이스 * 동작 (목록 [START STOP : 공정]) 와 거의 동일한 파라미터들을 갖는다. 인터레이스는 루프 내에서 요구되는 경우, 긴 전달의 진행에 제 2 스텝 사이즈 파라미터 값으로서 제공된다 (기본 1) *.

from itertools import islice

def parse_titles_v2(filename):
    with open(filename, 'r') as fp:
        # 设置 step=2,跳过无意义的 '---' 分隔符
        for line in islice(fp, 0, None, 2):
            yield line.strip()

대안 break 문 takewhile 3.

때때로, 우리는 각주기의 시작에,주기가 조기 종료 여부를 결정해야합니다. 예를 들어, 다음과 같은 :

for user in users:
    # 当第一个不合格的用户出现后,不再进行后面的处理
    if not is_qualified(user):
        break

    # 进行处理 ... ...

사전에 이러한 중단의 순환을 위해, 우리는 사용할 수 takewhile () 을 단순화하는 기능. takewhile(predicate, iterable)반복이됩니다 iterable호출하는 과정에서 매개 변수로 현재 개체를 계속 사용할 predicate현재 객체가 생성, 기능 테스트 및 함수가 true를 돌려주는 경우, 그 결과를 돌려주기가 계속됩니다. 그렇지 않으면, 바로 현재의주기를 방해.

사용 takewhile이 코드 샘플 :

from itertools import takewhile

for user in takewhile(is_qualified, users):
    # 进行处理 ... ...

이 밖에도 흥미로운 유틸리티 함수이며, 그들이 개체 복수 등을 순환하면서 기능 zip_longest을 사용하여, 사용주기, 플랫 더블 체인 함수 중첩 루프를 이용하여 사용될 수 itertools.

제한된 공간, 여기에 내가 소개하지 않을거야. 당신이 관심이 있다면, 당신은 자신에 대해 자세히 알아 보려면 공식 문서로 이동할 수 있습니다.

4. 자신의 수정 기능을 작성하는 발전기를 사용하여

이러한 기능 외에도이 제공 itertools에서, 우리는 아주 쉽게 자신의주기 변경 함수를 정의 빌더를 사용할 수 있습니다.

의 예를 들어, 간단한 함수를 보자 :

def sum_even_only(numbers):
    """对 numbers 里面所有的偶数求和"""
    result = 0
    for num in numbers:
        if num % 2 == 0:
            result += num
    return result

상기 함수에서, 순서로 루프의 본체 부가 모든 홀수 도입 걸러 if판정 문. 루프 내용을 단순화하기 위해, 우리는 특별한 함수 발생기 필터의 짝수를 정의 할 수 있습니다 :

def even_only(numbers):
    for num in numbers:
        if num % 2 == 0:
            yield num


def sum_even_only_v2(numbers):
    """对 numbers 里面所有的偶数求和"""
    result = 0
    for num in even_only(numbers):
        result += num
    return result

numbers사용하여 변수 even_only장식 기능은 sum_even_only_v2내부 기능은 논리를 "심지어 필터링"에 초점을 계속해야하고, 간단하게 완료 요약하지 않습니다.

힌트 : 물론, 위의 기능은 정말 실용적되지 않습니다. 현실 세계에서 직접 발전기에 가장 적합한이 간단한 수요 / 표현의 목록을 얻을 : sum(num for num in numbers if num % 2 == 0)

제안 2 : 생체 내 기능 코드 블록을 순환하여 복잡한 해체

나는 항상주기, 검은 마법을 열었을 때 당신이 새로운 루프 코드 블록을 쓸 때마다, 배열 내의 모든 내용을 반복 끝이 시작됩니다 더 멋진 일이라고 생각했다.

그러나 나는 또한 장점 이외에 마술의이 조각이 발견 그것은 또한 당신이 진나이가 잘못된 요소를 필터링 등의 데이터, 인쇄 로그를 사전 처리하는 등, 점점 더 많은 코드를 압착 유혹 할 것입니다. 일부는 심지어 같은 추상적 인 내용에 속하지 않는 한, 검은 마법의 같은 부분에 박제됩니다.

당신은이 과정의 모든 문제입니다 생각, 우리는 긴급한 필요 버프 배열에 있습니다. 당신은 그들이 갈 넣을 수 있습니다 루프의 박제 몸에 로직을 많이 넣어하지 않는 경우?

의 다음과 같은 비즈니스 시나리오를 살펴 보자. 웹 사이트에서 스크립트의 실행 기간 한 번 한 30 일이, 그 작업은 사용자가 로그 오프 일정 시간마다 주말에, 지난 30 일을 조회 한 다음 자신의 보너스 포인트를 보내는 것입니다.

다음과 같이 코드입니다 :

import time
import datetime


def award_active_users_in_last_30days():
    """获取所有在过去 30 天周末晚上 8 点到 10 点登录过的用户,为其发送奖励积分
    """
    days = 30
    for days_delta in range(days):
        dt = datetime.date.today() - datetime.timedelta(days=days_delta)
        # 5: Saturday, 6: Sunday
        if dt.weekday() not in (5, 6):
            continue

        time_start = datetime.datetime(dt.year, dt.month, dt.day, 20, 0)
        time_end = datetime.datetime(dt.year, dt.month, dt.day, 23, 0)

        # 转换为 unix 时间戳,之后的 ORM 查询需要
        ts_start = time.mktime(time_start.timetuple())
        ts_end = time.mktime(time_end.timetuple())

        # 查询用户并挨个发送 1000 奖励积分
        for record in LoginRecord.filter_by_range(ts_start, ts_end):
            # 这里可以添加复杂逻辑
            send_awarding_points(record.user_id, 1000) 

위 함수는 주로 2 사이클 구성된다. 외부 루프, 주로 획득 시간의 책임은 지난 30 일의 요구 사항을 충족하고, UNIX 타임 스탬프로 변환합니다. 적분 내부 루프 송신에서 이러한 두 타임 스탬프를 사용한 후.

앞서 언급 된 바와 같이, 플러그 외부 루프 블랙 매직 가장자리로 개방된다. 그러나 관찰을 통해, 우리가 찾을 수있는 루프의 몸 전체가 실제로 구성된 두 개의 전혀 관계가없는 작업으로 구성되어 있습니다 : "날짜 및 시간 스탬프가 준비 선택"과 "보너스 포인트를 보내기" .


복잡한 루프의 새로운 요구에 대처하는 방법

이 코드는 어떤 해는 무엇입니까? 말해 보자.

어느 날, 일부 사용자는 여전히 우리의 웹 사이트를 브러시, 주말에 밤에 잠을하지 않는 제품을 찾고, 우리는 그들이 그들 일찍 후 침대에 놓아 통지주고있다. 그래서 새로운 요구가 등장 : "지난 30 일 동안 5시에서 3시까지 알림을 보내 주말에 로그온 한 사용자에게" .

새로운 문제는 돌발했다. 당신 같은 샤프, 확실히 하나는이 새로운 수요가 사용자의 일부를 상영 것을 찾을 수 있습니다 수요가 매우 유사하다 전에. 그런 다음 루프의 몸을 살펴보기 전에 해당 그룹을 열 경우 내부 루프는 완전히 다른 논리가 있기 때문에, 당신은 코드를 다시 사용할 수 없습니다 단순히를 찾을 결합 함께. ☹️

컴퓨터 세계에서, 우리는 종종 사물 사이의 관계를 표현하기 위해 ** ** "결합"이라는 단어를 사용합니다. 위의 예에서, * "시간을 따기" "통합 보내기"* 같은 루프 본문에 살고있는이 두 가지, 아주 강한 결합 관계를 설립했다.

더 좋은 코드 재사용을 위해, 우리는 루프의 몸에서 분리 * 섹션 "시간을 따기"는 *에서 작동해야합니다. 그리고 우리의 오랜 친구, ** "발전기 기능은"**이 작업을 수행 할 수있는 최선의 선택입니다.

발전기 기능 분리 루프를 사용

해야 "시간 선택" 내부 루프 밖으로에서 분리 된 부분을, 우리는 새로운 발전기 기능을 정의 할 필요가 gen_weekend_ts_ranges()특별히 요구되는 UNIX 타임 스탬프를 생성하도록 설계를 :

def gen_weekend_ts_ranges(days_ago, hour_start, hour_end):
    """生成过去一段时间内周六日特定时间段范围,并以 UNIX 时间戳返回
    """
    for days_delta in range(days_ago):
        dt = datetime.date.today() - datetime.timedelta(days=days_delta)
        # 5: Saturday, 6: Sunday
        if dt.weekday() not in (5, 6):
            continue

        time_start = datetime.datetime(dt.year, dt.month, dt.day, hour_start, 0)
        time_end = datetime.datetime(dt.year, dt.month, dt.day, hour_end, 0)

        # 转换为 unix 时间戳,之后的 ORM 查询需要
        ts_start = time.mktime(time_start.timetuple())
        ts_end = time.mktime(time_end.timetuple())
        yield ts_start, ts_end

이 발전기 기능으로, "알림 보내기"와 새로운 요구 "보너스 포인트를 보내기"에 대한 이전 요구, 당신은 작업을 완료하는 데 루프의 몸을 다시 사용할 수 있습니다 :

def award_active_users_in_last_30days_v2():
    """发送奖励积分"""
    for ts_start, ts_end in gen_weekend_ts_ranges(30, hour_start=20, hour_end=23):
        for record in LoginRecord.filter_by_range(ts_start, ts_end):
            send_awarding_points(record.user_id, 1000)


def notify_nonsleep_users_in_last_30days():
    """发送通知"""
    for ts_start, ts_end in gen_weekend_ts_range(30, hour_start=3, hour_end=6):
        for record in LoginRecord.filter_by_range(ts_start, ts_end):
            notify_user(record.user_id, 'You should sleep more')

개요

이 글에서, 우리는 먼저 간단하게 "진짜."재활용 코드의 정의를 설명 그리고 그는 처음 제안을했다 : 혈액 순환을 개선하기 위해 수정 된 기능을 사용합니다. 가상 나 비즈니스 시나리오 후, 언론의 코드 내에서 듀티 사이클을 해체의 중요성을 설명.

핵심 사항을 요약한다 :

  • 함수는 환상 객체 자체를 사용하여 변형되고 개선 될 수있다 루프 본체 코드
  • 많은 도구가 있습니다 기능이 순환을 개선하는 데 사용할 수 있습니다 itertools
  • 발전기 기능을 사용하는 것은 쉽게 자신의 함수를 정의하기 위해 수정할 수 있습니다
  • 루프 내부에서, A는 매우 "코드를 부풀게는"사이트가 발생입니다
  • 디커플링 다른 코드 블록의 듀티 사이클의 생성 기능, 더 나은 유연성을 사용하여

텐센트 기술 엔지니어링 거의 친구를 알게되었습니다. 이 숫자는 스카우트의 친구뿐만 아니라 연구자들에게 최신 기술 기사 거위 공장을 제공합니다 전문 기술 매니아, 통찰력 기술 주제를 제공하는 목적으로, 특히 국경 탐사, 컴퓨터 기술과 인터넷 관련 주제 영역을 기반으로 기술 생태계를 구축, 토론에 참여하고 개방 된 플랫폼을 제공합니다.

텐센트 기술 공학 미래는 우리가 스카우트의 친구에 대한 혼란 기술적 인 작업에 응답 탱크가 생각하는, 거위 공장 기술자의 대부분을 초대뿐만 아니라 "좋아요", 우리를 들여 신중하게 준비하는 우리를 위해 포인트를 많이 대답하시기 바랍니다 것입니다 보통주 및 진행 바랍니다. 어떤 제안이 개인 편지 우리를하시기 바랍니다!

더 많은 기술적 건조 하시기 바랍니다 지속적인 관심 "텐센트 기술 및 엔지니어링은" 거의 번호를 알고, 우리의 칼럼에 가입 "텐센트 기술" .

추천

출처www.cnblogs.com/focus-z/p/10990175.html