{"id":37078149,"url":"https://github.com/marpple/partial.py","last_synced_at":"2026-01-14T09:03:58.190Z","repository":{"id":57450899,"uuid":"91290671","full_name":"marpple/partial.py","owner":"marpple","description":"Functional Python Library","archived":true,"fork":false,"pushed_at":"2017-06-28T09:02:10.000Z","size":441,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-11-12T18:23:12.639Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://marpple.github.io/partial.py/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marpple.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-15T03:17:07.000Z","updated_at":"2023-01-28T07:19:15.000Z","dependencies_parsed_at":"2022-09-10T11:02:07.115Z","dependency_job_id":null,"html_url":"https://github.com/marpple/partial.py","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/marpple/partial.py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2Fpartial.py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2Fpartial.py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2Fpartial.py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2Fpartial.py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marpple","download_url":"https://codeload.github.com/marpple/partial.py/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2Fpartial.py/sbom","scorecard":{"id":621032,"data":{"date":"2025-08-11","repo":{"name":"github.com/marpple/partial.py","commit":"92fba71a9b26145fec4fde4288912c8287a9a7b0"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T05:13:36.962Z","repository_id":57450899,"created_at":"2025-08-21T05:13:36.962Z","updated_at":"2025-08-21T05:13:36.962Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28414737,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:38:59.149Z","status":"ssl_error","status_checked_at":"2026-01-14T08:38:43.588Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-14T09:03:56.327Z","updated_at":"2026-01-14T09:03:58.182Z","avatar_url":"https://github.com/marpple.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Functional Python Library - Partial.py\n\n[Site](https://marpple.github.io/partial.py/) | [Docs](https://marpple.github.io/partial.py/docs)\n\nPartial.py는 함수형 파이썬을 더 많은 영역에서 사용하고자, 몇 가지 기능을 확장한 라이브러리입니다. Partial.py는 부분 적용, 파이프라인, 비동기 제어 등의 기능을 제공하고 있습니다.\n\n## 설치하기\n\n### Partial.py 설치\n\n##### git으로 설치하기:\n\n```bash\n$ git clone https://github.com/marpple/partial.py.git\n$ cd partial.py\n$ sudo python setup.py install\n```\n\n__혹은__\n\n##### pypi로 설치하기:\n\n```bash\n$ pip install partial.py\n```\n\n### Partial.py 사용\n\n네임스페이스로 사용할 `_`, `__`, `___`를 불러옵니다.\n\n```python\nfrom partial import _, __, ___\n```\n\n## 더 나은 부분 적용 (Partial application)\n\nPartial.py는 이름처럼 부분 적용(Partial application)을 중요하게 생각합니다. 기존의 `_.partial` 함수는 왼쪽에서부터만 인자를 적용해둘 수 있습니다. Partial.py의 `_.partial` 함수는 맨 오른쪽 인자나 맨 오른쪽에서 두 번째에만 인자를 적용해두는 것도 가능하며, 새로운 구분자인 `___`를 활용하여 중간 지점에 인자가 가변적으로 적용되도록 비워둘 수 있습니다.\n\n### _.partial의 일반적 사용\n\n`_.partial`을 실행하면서 인자 자리에 `_`를 넘기면 부분 적용할 인자를 건너띌 수 있습니다. `_`를 이용하면 원하는 곳에만 인자를 부분 적용해둘 수 있습니다. `_`가 있는 자리는 이후 실행시 채워집니다.\n\n```python\npc = _.partial(print, 1)\npc(2)\n# 결과: 1 2\n# 2 가 오른쪽으로 들어갑니다.\npc(2, 3)\n# 결과: 1 2 3\n# 2, 3이 오른쪽으로 들어갑니다.\n\npc = _.partial(print, _, 2)\npc(1)\n# 결과: 1 2\n# 1이 왼쪽의 _ 자리에 들어갑니다.\npc(1, 3)\n# 결과: 1 2 3\n# 1이 왼쪽의 _ 자리에 들어가고 3이 오른쪽으로 들어갑니다.\n\npc = _.partial(print, _, _, 3)\npc(1)\n# 결과: 1 undefined 3\n# 1이 왼쪽의 _ 자리에 들어가고 두 번째 _는 들어오지 않아 undefined가 됩니다.\npc(1, 2)\n# 결과: 1 2 3\n# 1과 2가 순서대로 _, _를 채웁니다.\npc(1, 2, 4)\n# 결과: 1 2 3 4\n# 1과 2가 순서대로 _, _를 채우고 3의 오른쪽으로 4가 들어갑니다.\n\npc = _.partial(print, _, 2, _, 4)\npc(1, 3, 5)\n# 결과: 1 2 3 4 5\n# 1을 _ 자리에 채우고 2를 넘겨서 _에 3을 채우고 4의 오른쪽에 5가 들어갑니다.\n\npc = _.partial(print, _, 2, _, _, 5)\npc(1, 3, 4, 6)\n# 결과: 1 2 3 4 5 6\n# 1을 _ 자리에 채우고 2를 넘겨서 _에 3을 채우고 다음 _에 4를 채우고 5의 오른쪽에 6이 들어갑니다.\n```\n\n### 오른쪽에서부터 인자 적용해두기\n\n`_.partial`을 실행하면 `___`를 기준으로 왼편의 인자들을 왼쪽부터 적용하고 오른편의 인자들을 오른쪽부터 적용할 준비를 해둔 함수를 리턴합니다. 부분 적용된 함수를 나중에 실행하면 그때 받은 인자들로 왼쪽과 오른쪽을 먼저 채운 후, 남은 인자들로 가운데 `___` 자리를 채웁니다.\n\n```python\npc = _.partial(print, ___, 2, 3)\npc(1)\n# 결과: 1 2 3\n# ___ 자리에 1이 들어가고 2, 3은 맨 오른쪽에 들어갑니다.\npc(1, 4, 5, 6)\n# 결과: 1 4 5 6 2 3\n# ___ 자리에 1, 4, 5, 6이 들어가고 2, 3은 맨 오른쪽에 들어갑니다.\n\npc = _.partial(print, _, 2, ___, 6)\npc(1, 3, 4, 5)\n# 결과: 1 2 3 4 5 6\n# _에 1이 들어가고 2를 넘어가고 ___ 자리에 3, 4, 5가 채워지고 6이 맨 오른쪽에 들어갑니다.\npc(1, 3, 4, 5, 7, 8, 9)\n# 결과: 1 2 3 4 5 7 8 9 6\n# _에 1이 들어가고 2를 넘어가고 ___ 자리에 3, 4, 5, 7, 8, 9가 채워지고 6이 맨 오른쪽에 들어갑니다.\n\npc = _.partial(print, _, 2, ___, 5, _, 7)\npc(1)\n# 결과: 1 2 5 undefined 7\n# _ 자리에 1이 들어가고 2와 5사이는 유동적이므로 모이고 5가 들어간 후 _가 undefined로 대체 되고 7이 들어갑니다.\npc(1, 3, 4)\n# 결과: 1 2 3 5 4 7\n# _ 자리에 1이 들어가고 2와 5사이에 3이 들어가고 _ 를 4로 채운 후 7이 들어갑니다.\n# 왼쪽의 _ 들이 우선 순위가 제일 높고 ___ 보다 오른쪽의 _ 들이 우선순위가 높습니다.\npc(1, 3, 4, 6, 8)\n# 결과: 1 2 3 4 6 5 8 7\n# _ 자리에 1이 들어가고 2와 5사이에 3, 4, 6이 들어가고 _ 를 8로 채운 후 7이 들어갑니다.\n```\n\n### 간결하게 사용하기\n\n`_ == _.partial`입니다. `_.partial`을 `_`로 간결하게 표현할 수 있습니다.\n\n```python\ndef add (a, b): \n  return a + b\n  \nadd10 = _(add, 10)\nprint( add10(5) )\n# 15\n```\n\n## 파이프라인\n\n파이프라인 함수인 `_.pipe`, `_.go` 등은 작은 함수들을 모아 큰 함수를 만드는 함수입니다. 파이프라인으로 함수를 조합하면 왼쪽에서부터 오른쪽, 위에서부터 아래로 표현되어 읽기 쉬운 코드가 됩니다. 체인 방식과 다르게 아무 함수나 사용할 수 있어 자유도가 높습니다. 작은 함수들을 인자와 결과만을 생각하면서 조합하면 됩니다.\n\n### 즉시 실행과 Multiple Results\n\n`_.go`는 파이프라인의 즉시 실행 버전입니다. 첫 번째 인자로 받은 값을 두 번째 인자로 받은 함수에게 넘겨주고 두 번째 인자로 받은 함수의 결과는 세 번째 함수에게 넘겨주는 것을 반복하다가 마지막 함수의 결과를 리턴해줍니다.\n\n```python\n_.go(10, # 첫 번째 함수에서 사용할 인자\n  lambda a: a * 10, # 연속 실행할 함수 1\n  # 100\n  lambda a: a - 50, # 연속 실행할 함수 2\n  # 50\n  lambda a: a + 10) # 연속 실행할 함수 3\n  # 60\n```\n\n`_.go`는 Multiple Results를 지원합니다. `_.mr` 함수를 함께 사용하면 다음 함수에게 2개 이상의 인자들을 전달할 수 있습니다.\n\n```python\n_.go(10, # 첫 번째 함수에서 사용할 인자\n  lambda a: _.mr(a * 10, 50), # 두 개의 값을 리턴\n  lambda a, b: a - b, # 두 개의 인자 받기\n  lambda a: a + 10)\n  # 60\n```\n\n`_.go`의 첫 번째 인자는 두 번째 인자인 함수가 사용할 인자고 두 번째 부터는 파이프라인에서 사용할 함수들입니다. `_.go`의 두 번째 인자인 함수, 즉 최초 실행될 함수에게 2개 이상의 인자를 넘기고자 한다면 `_.mr`을 사용하면 됩니다. `_.mr`로 인자들을 감싸서 넘겨주면, 다음 함수는 인자를 여러 개로 펼쳐서 받게 됩니다.\n\n```python\n_.go(_.mr(2, 3),\n  lambda a, b: a + b, # 2 + 3\n  lambda a: a * a)\n  # 25\n```\n\n`_.go`를 이미 정의되어 있는 함수와 조합하면 더욱 읽기 좋아집니다.\n\n```python\ndef add(a, b):\n  return a + b\n  \ndef square(a):\n  return a * a\n  \n_.go(_.mr(2, 3), add, square)\n# 25\n```\n\n### 파이프라인 함수를 리턴하는 _.pipe\n\n`_.go`가 즉시 실행하는 파이프라인이라면 `_.pipe`는 실행할 준비가 된 함수를 리턴하는 파이프라인 함수입니다. 그외 모든 기능은 `_.go`와 동일합니다.\n\n```python\nf1 = _.pipe(add, square)\nf1(2, 3)\n# 25\n```\n\n## 부분 커링\n\n### 커링이 부분적으로 동작하는 함수\n\nPartial.py의 주요 함수들은 커링이 부분적으로 동작하도록 지원하고 있습니다. 아래는 일반적인 사용 모습입니다.\n\n##### 일반적인 방식:\n\n```python\nvalues = lambda data: _.map(data, lambda v, *r: v)\nprint(values({ 'a': 1, 'b': 2, 'c': 4 }))\n# [1, 2, 4]\n\ntake3 = lambda data: _.take(data, 3)\ntake3([1, 2, 3, 4, 5])\n# [1, 2, 3]\n```\n\nPartial.py의 주요 함수들은 부분 커링이 적용되어 위와 동일한 동작을 아래와 같이 간결하게 표현할 수 있습니다.\n\n##### 부분 커링이 지원될 경우:\n\n```python\nvalues = _.map(lambda v, *r: v)\nprint(values({ 'a': 1, 'b': 2, 'c': 4 }))\n# [1, 2, 4]\n\ntake3 = _.take(3)\ntake3([1, 3, 5, 7, 9])\n# [1, 3, 5]\n```\n\n### 파이프라인과 함께\n\n부분 커링이 지원되면 파이프라인과 함께 사용할 때, 체인처럼 간결한 표현이 가능합니다.\n\n```python\nusers = [\n  { 'id': 1, 'name': 'ID', 'age': 32 },\n  { 'id': 2, 'name': 'HA', 'age': 25 },\n  { 'id': 3, 'name': 'BJ', 'age': 32 },\n  { 'id': 4, 'name': 'PJ', 'age': 28 },\n  { 'id': 5, 'name': 'JE', 'age': 27 },\n  { 'id': 6, 'name': 'JM', 'age': 32 },\n  { 'id': 7, 'name': 'JI', 'age': 31 }\n]\n\n## 일반적인 사용\n_.go(users,\n  lambda users: _.filter(users, lambda u, *r: u['age'] \u003c 30),\n  lambda users: _.pluck(users, 'name'),\n  print)\n# ['HA', 'PJ', 'JE']\n\n## 부분 커링이 된다면\n_.go(users,\n  _.filter(lambda u, *r: u['age'] \u003c 30),\n  _.pluck('name'),\n  print)\n# ['HA', 'PJ', 'JE']\n\n## Underscore.py 체인\nunderscore\\\n  .chain(users)\\\n  .filter(lambda u: u['age'] \u003c 30)\\\n  .pluck('name')\\\n  .tap(print)\n# ['HA', 'PJ', 'JE']\n```\n\n`_.go`, `_.pipe` 등의 파이프라인이 받는 재료는 함수이기 때문에 아무 함수나 조합할 수 있습니다. 체인처럼 메서드 등으로 준비되어있지 않아도 되며 Partial.py의 함수만 사용할 필요도 없습니다. Partial.py의 파이프라인은 결과를 여러 개로 리턴할 수 있고, 여러 개의 인자를 받을 수 있고, 다른 라이브러리에 있는 함수든, 직접 만든 함수든, 익명 함수든 모두 쉽게 사용할 수 있습니다.\n\n```python\nproducts = [\n  { 'id': 1, 'name': '후드 집업', 'discounted_price': 6000, 'price': 10000  },\n  { 'id': 2, 'name': '코잼 후드티', 'discounted_price': 8000, 'price': 8000  },\n  { 'id': 3, 'name': 'A1 반팔티', 'discounted_price': 6000, 'price': 6000  },\n  { 'id': 4, 'name': '코잼 반팔티', 'discounted_price': 5000, 'price': 6000  }\n]\n\n# 할인 상품들을 가격이 낮은 순으로 정렬한 상품 이름들\n_.go(products,\n  _.filter(lambda p, *r: p['price'] \u003e p['discounted_price']),\n  _.sortBy('discounted_price'),\n  _.pluck('name'),\n  print)\n  # ['코잼 반팔티', '후드 집업']\n\n# 할인이 없는 상품들의 id들\n_.go(products,\n  _.reject(lambda p, *r: p['price'] \u003e p['discounted_price']),\n  _.pluck('id'),\n  print)\n  # [2, 3]\n\n# 할인 상품 중 할인액이 가장 낮은 상품의 이름\n_.go(products,\n  _.filter(lambda p, *r: p['price'] \u003e p['discounted_price']),\n  _.min(lambda p, *r: p['price'] - p['discounted_price']),\n  _.val('name'),\n  print)\n  # 코잼 반팔티\n\n# 할인액이 가장 높은 상품의 이름\n_.go(products,\n  _.max(lambda p, *r: p['price'] - p['discounted_price']),\n  _.val('name'),\n  print)\n  # 후드 집업\n```\n\n## 비동기\n\n`async`, `await` 키워드와 Partial.py를 함께 사용하면 다양한 비동기 상황을 간단히 제어할 수 있습니다.\n\n### 파이프라인으로 비동기 제어 하기\n\n`_.asy`를 네임스페이스로 갖는 `_.asy.go`, `_.asy.pipe` 등의 파이프라인 함수들은 비동기 제어를 지원합니다.\n\n##### await 키워드와 함께 사용:\n\n```python\nasync def asy_add(val, *r):\n  await asyncio.sleep(1)\n  return val + 10\n  \n## await 키워드를 사용해서 바로 실행하는 _.go.asy\nawait _.asy.go(10,\n  asy_add,\n  print)\n  # 20\n  \n## _.asy.pipe로 비동기 함수와 동기 함수 조합해 만든 새로운 함수\nasy_pipe = _.asy.pipe(asy_add, print)\nawait asy_pipe(10) \n# 20\n\n## _.pipe는 비동기 함수를 만나면 자동으로 _.asy.pipe가 됩니다.\nasy_pipe = _.pipe(asy_add, print)\nawait asy_pipe(10)\n# 20\n```\n\n### 컬렉션을 다루는 비동기 제어 함수\n\nPartial.py의 `_.each`, `_.map`, `_.reduce` 등의 주요 함수들은 `_.asy.go`와 `_.asy.pipe`처럼 동기와 비동기 상황이 모두 대응되도록 되어 있습니다. Partial.py의 함수를 이용하면 비동기 상황에서도 동기 상황과 동일한 코드를 작성할 수 있고, 비동기 함수와 동기 함수의 조합도 가능합니다.\n\n```python\nasync def asyncDate(*r):\n  await asyncio.sleep(1)\n  return datetime.datetime.now()\n\ndef syncDate(*r):\n  return datetime.datetime.now()\n\n_.go(\n  [1, 2, 3],\n  _.map(syncDate),\n  _.map(lambda now, *r: now.strftime('%Y-%m-%d %H:%M:%S')),\n  print)\n# ['2017-06-16 12:34:39', '2017-06-16 12:34:39', '2017-06-16 12:34:39']\n\nawait _.asy.go(\n  [1, 2, 3],\n  _.map(asyncDate),\n  _.map(lambda now, *r: now.strftime('%Y-%m-%d %H:%M:%S')),\n  print)\n# ['2017-06-16 12:34:39', '2017-06-16 12:34:39', '2017-06-16 12:34:39']\n```\n\n위 사례처럼 Partial.py는 `_.pipe`, `_.each`, `_.map`, `_.find`, `_.filter`, `_.reject`, `_.reduce`, `_.some`, `_.every` 등의 함수들에서 자동 비동기 제어 로직을 지원합니다.\n\n\n## 지연 평가 L\n\nPartial.py의 `L`을 이용하면, 파이프라인 내부에서 함수들의 실행 순서를 재배치하여 적절하게 평가를 지연합니다. 사용법은 간단합니다. Partial.py에서 `L`을 `import`하면 됩니다. `L`을 통해 지연 평가할 영역을 명시적으로 선택할 수 있습니다. `_.go, _.pipe`등의 파이프라인이 `L`로 시작하여 `L`로 끝날 때까지의 함수들을 재배치하여 성능을 개선합니다.\n\n### 비교\n\n##### 엄격한 평가:\n```python\nlist = [1, 2, 3, 4, 5, 6]\n_.go(list,\n  _.map(lambda v, *r: v * v), # 6번\n  _.filter(lambda v, *r: v \u003c 20), # 6번\n  _.take(2),\n  print)\n# [1, 4]\n# 총 12번\n```\n\n##### 지연 평가:\n```python\nlist = [1, 2, 3, 4, 5, 6]\n_.go(list,\n  L.map(lambda v, *r: v * v), # 2번\n  L.filter(lambda v, *r: v \u003c 20), # 2번\n  L.take(2),\n  print)\n# [1, 4]\n# 총 4번  \n```\n\n### 지원 함수들\n\nPartial.js의 지연 평가 지원 함수로는 `L.map`, `L.filter`, `L.reject`, `L.find`, `L.some`, `L.every`, `L.take`가 있습니다. 이 함수들을 순서대로 나열하면 파이프라인이 평가 시점을 변경하여 성능을 개선합니다.\n\n다음과 같은 상황 등에서 동작합니다.\n\n- map-\u003emap-\u003emap\n- map-\u003etake\n- filter-\u003etake\n- map-\u003efilter-\u003etake\n- map-\u003efilter-\u003emap-\u003emap\n- map-\u003efilter-\u003emap-\u003etake\n- map-\u003ereject-\u003emap-\u003emap-\u003efilter-\u003emap\n- map-\u003esome\n- map-\u003eevery\n- map-\u003efind\n- map-\u003efilter-\u003esome\n- map-\u003efilter-\u003eevery\n- map-\u003efilter-\u003efind\n- filter-\u003emap-\u003esome\n- filter-\u003emap-\u003eevery\n- filter-\u003emap-\u003ereject-\u003efind\n\n지연 평가를 시작시키고 유지 시키는 함수는 `map`, `filter`, `reject`이고 끝을 내는 함수는 `take`, `some`, `every`, `find`, 입니다.\n\n```python\nusers = [\n  { 'id': 1, 'name': 'ID', 'age': 12 },\n  { 'id': 2, 'name': 'BJ', 'age': 28 },\n  { 'id': 3, 'name': 'HA', 'age': 13 },\n  { 'id': 4, 'name': 'PJ', 'age': 23 },\n  { 'id': 5, 'name': 'JE', 'age': 29 },\n  { 'id': 6, 'name': 'JM', 'age': 32 },\n  { 'id': 7, 'name': 'JE', 'age': 31 },\n  { 'id': 8, 'name': 'HI', 'age': 15 },\n  { 'id': 9, 'name': 'HO', 'age': 28 },\n  { 'id': 10, 'name': 'KO', 'age': 34 }\n]\n\n# 10대 2명까지만 찾아내기\n_.go(users,\n  L.filter(lambda user, *r : user['age'] \u003c 20),\n  L.take(2),\n  print)\n# [{ 'id': 1, 'name': 'ID', 'age': 12 }, { 'id': 3, 'name': 'HA', 'age': 13 }]\n# 3번만 반복\n\n# 10대 2명까지만 찾아내서 이름 수집하기\n_.go(users,\n  L.filter(lambda user, *r : user['age'] \u003c 20),\n  L.map(lambda v, *r : v['name']),\n  L.take(2),\n  print)\n# ['ID', 'HA']\n# 3번만 반복\n```\n\n### L.strict\n\n`L.strict`를 이용하여 지연 평가를 동작시킬 것인가를 동적으로 변경할 수 있습니다.\n\n##### 숫자로 하기:\n```python\nstrict_or_lazy1 = __(\n  _.range,\n  L.strict(100),\n  L.map(lambda v, *r: v * v),\n  L.filter(lambda v, *r: _.bool(v % 2)),\n  L.take(10),\n  print)\n\nstrict_or_lazy1(50)\n# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]\n# 50 번 반복 (염격)\n\nstrict_or_lazy1(100)\n# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]\n# 20 번 반복 (지연)\n\nstrict_or_lazy1(15)\n# [1, 9, 25, 49, 81, 121, 169]\n# 15 번 반복 (엄격)\n```\n\n##### 함수로 하기:\n```python\nstrict_or_lazy2 = __(\n  _.range,\n  L.strict(lambda list, *r : len(list) \u003c 100),\n  L.map(lambda v, *r : v * v),\n  L.filter(lambda v, *r : bool(v % 2)),\n  L.take(10),\n  print)\n\nstrict_or_lazy2(50)\n# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]\n# 50 번 반복 (염격)\n\nstrict_or_lazy2(100)\n# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]\n# 20 번 반복 (지연)\n\nstrict_or_lazy2(15)\n# [1, 9, 25, 49, 81, 121, 169]\n# 15 번 반복 (엄격)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarpple%2Fpartial.py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarpple%2Fpartial.py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarpple%2Fpartial.py/lists"}