Kho python

“Cấu trúc” có nghĩa là các quyết định bạn đưa ra liên quan đến cách dự án của bạn đáp ứng tốt nhất mục tiêu của nó. Chúng ta cần xem xét cách tận dụng tốt nhất các tính năng của Python để tạo mã rõ ràng, hiệu quả. Về mặt thực tế, “cấu trúc” có nghĩa là tạo mã sạch có logic và các phần phụ thuộc rõ ràng cũng như cách các tệp và thư mục được tổ chức trong hệ thống tệp

Những chức năng nào nên đi vào những mô-đun nào?

Trong phần này, chúng ta xem xét kỹ hơn các mô-đun của Python và các hệ thống nhập vì chúng là các yếu tố trung tâm để thực thi cấu trúc trong dự án của bạn. Sau đó, chúng tôi thảo luận về các quan điểm khác nhau về cách xây dựng mã có thể được mở rộng và kiểm tra một cách đáng tin cậy

Cấu trúc của Kho lưu trữ

Nó quan trọng

Giống như Kiểu mã, Thiết kế API và Tự động hóa là điều cần thiết cho một chu kỳ phát triển lành mạnh. Cấu trúc kho lưu trữ là một phần quan trọng trong kiến ​​trúc dự án của bạn

Khi một người dùng hoặc cộng tác viên tiềm năng truy cập vào trang của kho lưu trữ của bạn, họ sẽ thấy một số điều

  • Tên dự án
  • mô tả dự án
  • Tập tin Bunch O'

Chỉ khi họ cuộn xuống dưới màn hình đầu tiên thì người dùng mới thấy README của dự án của bạn

Nếu repo của bạn là một đống tệp khổng lồ hoặc một mớ hỗn độn các thư mục, họ có thể tìm ở nơi khác trước khi đọc tài liệu đẹp đẽ của bạn

Ăn mặc cho công việc bạn muốn, không phải công việc bạn có

Tất nhiên, ấn tượng đầu tiên không phải là tất cả. Bạn và đồng nghiệp của mình sẽ dành vô số thời gian làm việc với kho lưu trữ này, cuối cùng sẽ trở nên quen thuộc với mọi ngóc ngách. Bố cục là quan trọng

Kho lưu trữ mẫu

tl;dr. Đây là những gì Kenneth Reitz đề xuất vào năm 2013

Kho lưu trữ này có sẵn trên GitHub

README.rst
LICENSE
setup.py
requirements.txt
sample/__init__.py
sample/core.py
sample/helpers.py
docs/conf.py
docs/index.rst
tests/test_basic.py
tests/test_advanced.py

Hãy đi vào một số chi tiết cụ thể

Mô-đun thực tế

Địa điểm
./sample.py
9 hoặc
./test_sample.py
0Mục đíchMã quan tâm

Gói mô-đun của bạn là trọng tâm cốt lõi của kho lưu trữ. Nó không nên được giấu đi

./sample/

Nếu mô-đun của bạn chỉ bao gồm một tệp duy nhất, bạn có thể đặt nó trực tiếp vào thư mục gốc của kho lưu trữ của mình

./sample.py

Thư viện của bạn không thuộc thư mục con src hoặc python mơ hồ

Giấy phép

Địa điểm
./test_sample.py
1Mục đích Xây dựng luật

Đây được cho là phần quan trọng nhất trong kho lưu trữ của bạn, ngoài bản thân mã nguồn. Toàn bộ văn bản giấy phép và khiếu nại bản quyền phải tồn tại trong tệp này

Nếu bạn không chắc mình nên sử dụng giấy phép nào cho dự án của mình, hãy xem chọnalicense. com

Tất nhiên, bạn cũng có thể tự do xuất bản mã mà không cần giấy phép, nhưng điều này sẽ ngăn nhiều người có khả năng sử dụng hoặc đóng góp cho mã của bạn

Cài đặt. py

Địa điểm
./test_sample.py
2Mục đích Quản lý gói hàng và phân phối

Nếu gói mô-đun của bạn nằm ở thư mục gốc của kho lưu trữ, thì rõ ràng gói này cũng phải ở thư mục gốc

Tệp yêu cầu

Location
./test_sample.py
3PurposeDevelopment dependencies

A nên được đặt ở thư mục gốc của kho lưu trữ. Nó nên chỉ định các phụ thuộc cần thiết để đóng góp cho dự án. thử nghiệm, xây dựng và tạo tài liệu

Nếu dự án của bạn không có phụ thuộc phát triển hoặc nếu bạn muốn thiết lập môi trường phát triển thông qua

./test_sample.py
4, tệp này có thể không cần thiết

Tài liệu

Location
./test_sample.py
5PurposePackage reference documentation

Có rất ít lý do để điều này tồn tại ở nơi khác

Bộ kiểm tra

Để được tư vấn về cách viết bài kiểm tra của bạn, hãy xem Kiểm tra mã của bạn .

Địa điểm
./test_sample.py
6 hoặc
./test_sample.py
7Mục đíchTích hợp gói và kiểm tra đơn vị

Khi mới bắt đầu, một bộ thử nghiệm nhỏ thường sẽ tồn tại trong một tệp duy nhất

./test_sample.py

Khi bộ thử nghiệm phát triển, bạn có thể di chuyển các thử nghiệm của mình vào một thư mục, như vậy

tests/test_basic.py
tests/test_advanced.py

Rõ ràng, các mô-đun thử nghiệm này phải nhập mô-đun đã đóng gói của bạn để kiểm tra nó. Bạn có thể làm điều này một vài cách

  • Yêu cầu gói được cài đặt trong gói trang web
  • Sử dụng sửa đổi đường dẫn đơn giản (nhưng rõ ràng) để giải quyết gói đúng cách

Tôi đánh giá cao cái sau. Yêu cầu nhà phát triển chạy

./test_sample.py
8 để kiểm tra cơ sở mã thay đổi tích cực cũng yêu cầu họ phải thiết lập môi trường biệt lập cho từng phiên bản của cơ sở mã

Để cung cấp ngữ cảnh nhập cho các bài kiểm tra riêng lẻ, hãy tạo tệp

./test_sample.py
9

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample

Sau đó, trong các mô-đun thử nghiệm riêng lẻ, hãy nhập mô-đun như vậy

from .context import sample

Điều này sẽ luôn hoạt động như mong đợi, bất kể phương pháp cài đặt

Một số người sẽ khẳng định rằng bạn nên phân phối các bài kiểm tra của mình trong chính mô-đun của mình – tôi không đồng ý. Nó thường làm tăng độ phức tạp cho người dùng của bạn;

Makefile

Location
tests/test_basic.py
tests/test_advanced.py
0PurposeGeneric management tasks

Nếu bạn xem hầu hết các dự án của tôi hoặc bất kỳ dự án Pocoo nào, bạn sẽ nhận thấy một Makefile nằm xung quanh. Tại sao?

Makefile mẫu

init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test

Các tập lệnh quản lý chung khác (e. g.

tests/test_basic.py
tests/test_advanced.py
1 hoặc
tests/test_basic.py
tests/test_advanced.py
2) cũng thuộc thư mục gốc của kho lưu trữ

Về ứng dụng Django

Tôi đã nhận thấy một xu hướng mới trong các ứng dụng Django kể từ khi phát hành Django 1. 4. Nhiều nhà phát triển đang cấu trúc kho lưu trữ của họ kém do các mẫu ứng dụng đi kèm mới

Làm sao?

$ django-admin.py startproject samplesite

Cấu trúc kho lưu trữ kết quả trông như thế này

README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py

Đừng làm điều này

Các đường dẫn lặp đi lặp lại gây nhầm lẫn cho cả công cụ và nhà phát triển của bạn. Việc lồng nhau không cần thiết không giúp được gì cho bất kỳ ai (trừ khi họ hoài cổ về các kho lưu trữ SVN nguyên khối)

Hãy làm điều đó đúng cách

./sample/
0

Lưu ý “

tests/test_basic.py
tests/test_advanced.py
3”

Cấu trúc kết quả

./sample/
1

Cấu trúc mã là chìa khóa

Nhờ cách nhập và mô-đun được xử lý trong Python, việc cấu trúc một dự án Python tương đối dễ dàng. Dễ dàng ở đây có nghĩa là bạn không có nhiều ràng buộc và mô hình nhập mô-đun dễ nắm bắt. Do đó, bạn chỉ còn lại nhiệm vụ kiến ​​trúc thuần túy là tạo ra các phần khác nhau của dự án và các tương tác của chúng.

Cấu trúc dễ dàng của một dự án có nghĩa là nó cũng dễ dàng thực hiện nó một cách kém cỏi. Một số dấu hiệu của một dự án có cấu trúc kém bao gồm

  • Phụ thuộc vòng tròn nhiều và lộn xộn. Nếu các lớp Bàn ghế trong
    tests/test_basic.py
    tests/test_advanced.py
    
    4 cần nhập Carpenter từ
    tests/test_basic.py
    tests/test_advanced.py
    
    5 để trả lời một câu hỏi chẳng hạn như
    tests/test_basic.py
    tests/test_advanced.py
    
    6 và nếu ngược lại, lớp Carpenter cần nhập Bàn ghế để trả lời câu hỏi
    tests/test_basic.py
    tests/test_advanced.py
    
    7, thì bạn có một phụ thuộc vòng. Trong trường hợp này, bạn sẽ phải dùng đến các thủ thuật hack mỏng manh, chẳng hạn như sử dụng các câu lệnh nhập bên trong các phương thức hoặc chức năng của mình
  • khớp nối ẩn. Mỗi và mọi thay đổi trong quá trình triển khai của Table đều phá vỡ 20 bài kiểm tra trong các trường hợp kiểm tra không liên quan vì nó phá vỡ mã của Carpenter, điều này đòi hỏi phải phẫu thuật rất cẩn thận để thích ứng với thay đổi. Điều này có nghĩa là bạn có quá nhiều giả định về Bảng trong mã Carpenter hoặc ngược lại
  • Sử dụng nhiều trạng thái hoặc bối cảnh toàn cầu. Thay vì truyền rõ ràng
    tests/test_basic.py
    tests/test_advanced.py
    
    8 cho nhau, Table và Carpenter dựa vào các biến toàn cục có thể được sửa đổi và được sửa đổi nhanh chóng bởi các tác nhân khác nhau. Bạn cần xem xét kỹ lưỡng tất cả quyền truy cập vào các biến toàn cầu này để hiểu tại sao một bảng hình chữ nhật lại trở thành một hình vuông và phát hiện ra rằng mã mẫu từ xa cũng đang sửa đổi ngữ cảnh này, làm rối tung các kích thước của bảng
  • mã spaghetti. nhiều trang chứa các mệnh đề if và vòng lặp lồng nhau với nhiều mã thủ tục được sao chép và không có phân đoạn thích hợp được gọi là mã spaghetti. Sự thụt lề có ý nghĩa của Python (một trong những tính năng gây tranh cãi nhất của nó) khiến cho việc duy trì loại mã này trở nên rất khó khăn. Tin tốt là bạn có thể không thấy quá nhiều
  • Mã Ravioli có nhiều khả năng bằng Python. nó bao gồm hàng trăm mảnh logic nhỏ giống nhau, thường là các lớp hoặc đối tượng, không có cấu trúc phù hợp. Nếu bạn không bao giờ có thể nhớ, nếu bạn phải sử dụng FurnitureTable, AssetTable hoặc Table, hoặc thậm chí TableNew cho nhiệm vụ của mình, thì bạn có thể đang bơi trong mã ravioli

mô-đun

Các mô-đun Python là một trong những lớp trừu tượng chính có sẵn và có lẽ là lớp tự nhiên nhất. Các lớp trừu tượng cho phép tách mã thành các phần chứa dữ liệu và chức năng liên quan

Ví dụ: một lớp của dự án có thể xử lý giao tiếp với hành động của người dùng, trong khi lớp khác sẽ xử lý thao tác dữ liệu ở mức độ thấp. Cách tự nhiên nhất để tách hai lớp này là tập hợp lại tất cả các chức năng giao tiếp trong một tệp và tất cả các hoạt động cấp thấp trong một tệp khác. Trong trường hợp này, tệp giao diện cần nhập tệp cấp thấp. Điều này được thực hiện với các câu lệnh

tests/test_basic.py
tests/test_advanced.py
9 và
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
0

Ngay khi bạn sử dụng câu lệnh nhập, bạn sử dụng các mô-đun. Đây có thể là các mô-đun tích hợp sẵn như os và sys, mô-đun bên thứ ba mà bạn đã cài đặt trong môi trường của mình hoặc mô-đun nội bộ của dự án của bạn

Để phù hợp với hướng dẫn về phong cách, hãy đặt tên mô-đun ngắn, viết thường và đảm bảo tránh sử dụng các ký hiệu đặc biệt như dấu chấm (. ) hoặc dấu chấm hỏi (?). Tên tệp như

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
1 là tên bạn nên tránh. Đặt tên theo cách này sẽ cản trở cách Python tìm kiếm các mô-đun

trong trường hợp của tôi. thư rác. py Python hy vọng sẽ tìm thấy tệp

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
2 trong thư mục có tên
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
3, điều này không đúng. Có một cách sử dụng ký hiệu dấu chấm trong tài liệu Python

Nếu muốn, bạn có thể đặt tên cho mô-đun của mình là

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
4, nhưng ngay cả dấu gạch dưới, người bạn đáng tin cậy của chúng ta, cũng không nên xuất hiện thường xuyên trong tên mô-đun. Tuy nhiên, việc sử dụng các ký tự khác (dấu cách hoặc dấu gạch ngang) trong tên mô-đun sẽ ngăn quá trình nhập (- là toán tử trừ). Cố gắng giữ tên mô-đun ngắn để không cần phải tách các từ. Và, trên hết, không có không gian tên với dấu gạch dưới;

./sample/
2

Ngoài một số hạn chế đặt tên, không có gì đặc biệt cần thiết để tệp Python trở thành một mô-đun. Nhưng bạn cần hiểu cơ chế nhập để sử dụng đúng khái niệm này và tránh một số vấn đề

Cụ thể, câu lệnh

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
5 sẽ tìm tệp thích hợp, đó là
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 trong cùng thư mục với người gọi, nếu nó tồn tại. Nếu nó không được tìm thấy, trình thông dịch Python sẽ tìm kiếm
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 trong “đường dẫn” theo cách đệ quy và đưa ra một ngoại lệ ImportError khi không tìm thấy nó

Khi tìm thấy

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6, trình thông dịch Python sẽ thực thi mô-đun trong một phạm vi biệt lập. Mọi câu lệnh cấp cao nhất trong
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 sẽ được thực hiện, bao gồm cả các lần nhập khác nếu có. Định nghĩa chức năng và lớp được lưu trữ trong từ điển của mô-đun

Sau đó, các biến, hàm và lớp của mô-đun sẽ có sẵn cho người gọi thông qua không gian tên của mô-đun, một khái niệm trung tâm trong lập trình đặc biệt hữu ích và mạnh mẽ trong Python

Trong nhiều ngôn ngữ, một lệnh

from .context import sample
0 được bộ tiền xử lý sử dụng để lấy tất cả mã được tìm thấy trong tệp và 'sao chép' mã đó vào mã của người gọi. Nó khác với Python. mã đi kèm được tách biệt trong một không gian tên mô-đun, điều đó có nghĩa là bạn thường không phải lo lắng rằng mã đi kèm có thể có tác dụng không mong muốn, e. g. ghi đè lên một chức năng hiện có cùng tên

Có thể mô phỏng hành vi tiêu chuẩn hơn bằng cách sử dụng cú pháp đặc biệt của câu lệnh nhập.

from .context import sample
1. Điều này thường được coi là thực hành xấu. Sử dụng
from .context import sample
2 làm cho mã khó đọc hơn và làm cho các phần phụ thuộc ít bị ngăn cách hơn

Sử dụng

from .context import sample
3 là một cách để xác định chức năng bạn muốn nhập và đặt nó vào không gian tên cục bộ. Mặc dù ít gây hại hơn nhiều so với
from .context import sample
2 vì nó hiển thị rõ ràng những gì được nhập trong không gian tên cục bộ, nhưng ưu điểm duy nhất của nó so với
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
5 đơn giản hơn là nó sẽ tiết kiệm được một chút thời gian gõ

Rất tệ

./sample/
3

Tốt hơn

./sample/
4

Tốt nhất

./sample/
5

Như đã đề cập trong phần này, khả năng đọc là một trong những tính năng chính của Python. Khả năng đọc có nghĩa là tránh văn bản soạn sẵn vô dụng và lộn xộn; . Nhưng sự ngắn gọn và tối nghĩa là giới hạn mà sự ngắn gọn nên dừng lại. Có thể biết ngay lập tức một lớp hoặc chức năng đến từ đâu, như trong thành ngữ

from .context import sample
6, giúp cải thiện đáng kể khả năng đọc và hiểu mã trong tất cả trừ các dự án tệp đơn giản nhất

gói

Python cung cấp một hệ thống đóng gói rất đơn giản, nó chỉ đơn giản là một phần mở rộng của cơ chế mô-đun cho một thư mục

Bất kỳ thư mục nào có tệp

from .context import sample
7 đều được coi là gói Python. Các mô-đun khác nhau trong gói được nhập theo cách tương tự như các mô-đun đơn giản, nhưng với một hành vi đặc biệt đối với tệp
from .context import sample
7, được sử dụng để thu thập tất cả các định nghĩa trên toàn gói

Tệp

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 trong thư mục
init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
0 được nhập với câu lệnh
init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
1. Câu lệnh này sẽ tìm tệp
from .context import sample
7 trong
init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
3 và thực hiện tất cả các câu lệnh cấp cao nhất của nó. Sau đó, nó sẽ tìm một tệp có tên
init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
4 và thực hiện tất cả các câu lệnh cấp cao nhất của nó. Sau các thao tác này, bất kỳ biến, hàm hoặc lớp nào được xác định trong
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 đều có sẵn trong gói. không gian tên modu

Một vấn đề thường thấy là thêm quá nhiều mã vào tệp

from .context import sample
7. Khi độ phức tạp của dự án tăng lên, có thể có các gói con và gói con trong cấu trúc thư mục sâu. Trong trường hợp này, việc nhập một mặt hàng từ gói con phụ sẽ yêu cầu thực thi tất cả các tệp
from .context import sample
7 đã gặp khi duyệt qua cây

Để trống tệp

from .context import sample
7 được coi là thông lệ bình thường và thậm chí là tốt, nếu các mô-đun và gói con của gói không cần chia sẻ bất kỳ mã nào

Cuối cùng, có sẵn một cú pháp thuận tiện để nhập các gói được lồng sâu.

init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
9. Điều này cho phép bạn sử dụng mod thay cho sự lặp lại dài dòng của
$ django-admin.py startproject samplesite
0

Lập trình hướng đối tượng

Python đôi khi được mô tả là ngôn ngữ lập trình hướng đối tượng. Điều này có thể gây hiểu nhầm và cần làm rõ thêm

Trong Python, mọi thứ đều là một đối tượng và có thể được xử lý như vậy. Đây là ý nghĩa khi chúng ta nói, ví dụ, các hàm là đối tượng hạng nhất. Hàm, lớp, chuỗi và thậm chí cả kiểu là các đối tượng trong Python. giống như bất kỳ đối tượng nào, chúng có một loại, chúng có thể được truyền dưới dạng đối số hàm và chúng có thể có các phương thức và thuộc tính. Theo cách hiểu này, Python có thể được coi là một ngôn ngữ hướng đối tượng

Tuy nhiên, không giống như Java, Python không áp đặt lập trình hướng đối tượng làm mô hình lập trình chính. Hoàn toàn khả thi đối với một dự án Python không hướng đối tượng, tôi. e. không sử dụng hoặc sử dụng rất ít định nghĩa lớp, kế thừa lớp hoặc bất kỳ cơ chế nào khác dành riêng cho ngôn ngữ lập trình hướng đối tượng

Ngoài ra, như đã thấy trong phần này, cách Python xử lý các mô-đun và không gian tên cung cấp cho nhà phát triển một cách tự nhiên để đảm bảo việc đóng gói và phân tách các lớp trừu tượng, cả hai đều là những lý do phổ biến nhất để sử dụng hướng đối tượng. Do đó, các lập trình viên Python có nhiều quyền hạn hơn để không sử dụng hướng đối tượng, khi nó không được yêu cầu bởi mô hình kinh doanh

Có một số lý do để tránh hướng đối tượng không cần thiết. Việc xác định các lớp tùy chỉnh rất hữu ích khi chúng ta muốn gắn một số trạng thái và một số chức năng lại với nhau. Vấn đề, như đã chỉ ra trong các cuộc thảo luận về lập trình chức năng, xuất phát từ phần "trạng thái" của phương trình

Trong một số kiến ​​trúc, điển hình là các ứng dụng web, nhiều phiên bản của các quy trình Python được sinh ra như một phản hồi cho các yêu cầu bên ngoài xảy ra đồng thời. Trong trường hợp này, việc giữ một số trạng thái trong các đối tượng được khởi tạo, có nghĩa là giữ một số thông tin tĩnh về thế giới, dễ xảy ra các vấn đề tương tranh hoặc điều kiện chủng tộc. Đôi khi, giữa việc khởi tạo trạng thái của một đối tượng (thường được thực hiện với phương thức

$ django-admin.py startproject samplesite
1) và việc sử dụng thực tế trạng thái của đối tượng thông qua một trong các phương thức của nó, thế giới có thể đã thay đổi và trạng thái được giữ lại có thể đã lỗi thời. Ví dụ: một yêu cầu có thể tải một mục trong bộ nhớ và đánh dấu nó là đã đọc bởi người dùng. Nếu một yêu cầu khác yêu cầu xóa mục này cùng lúc, thì việc xóa có thể thực sự xảy ra sau khi quá trình đầu tiên tải mục đó và sau đó chúng tôi phải đánh dấu đối tượng đã xóa là đã đọc

Vấn đề này và các vấn đề khác đã dẫn đến ý tưởng rằng sử dụng các hàm không trạng thái là một mô hình lập trình tốt hơn

Một cách khác để nói điều tương tự là đề xuất sử dụng các hàm và thủ tục với càng ít ngữ cảnh ngầm và tác dụng phụ càng tốt. Bối cảnh ẩn của hàm được tạo thành từ bất kỳ biến toàn cục hoặc mục nào trong lớp lưu trữ được truy cập từ bên trong hàm. Tác dụng phụ là những thay đổi mà một hàm tạo ra đối với ngữ cảnh ẩn của nó. Nếu một chức năng lưu hoặc xóa dữ liệu trong một biến toàn cục hoặc trong lớp lưu trữ lâu dài, thì nó được cho là có tác dụng phụ

Việc cô lập cẩn thận các chức năng có ngữ cảnh và tác dụng phụ khỏi các chức năng có logic (được gọi là các chức năng thuần túy) mang lại những lợi ích sau

  • Các chức năng thuần túy là xác định. đưa ra một đầu vào cố định, đầu ra sẽ luôn giống nhau
  • Các chức năng thuần túy dễ thay đổi hoặc thay thế hơn nhiều nếu chúng cần được cấu trúc lại hoặc tối ưu hóa
  • Các hàm thuần túy dễ kiểm tra hơn với các bài kiểm tra đơn vị. Ít cần thiết lập bối cảnh phức tạp và làm sạch dữ liệu sau đó
  • Các chức năng thuần túy dễ thao tác, trang trí và truyền lại hơn

Tóm lại, các hàm thuần túy là các khối xây dựng hiệu quả hơn các lớp và đối tượng đối với một số kiến ​​trúc vì chúng không có ngữ cảnh hoặc tác dụng phụ

Rõ ràng, hướng đối tượng là hữu ích và thậm chí cần thiết trong nhiều trường hợp, ví dụ như khi phát triển các ứng dụng hoặc trò chơi đồ họa trên máy tính để bàn, trong đó những thứ được thao tác (cửa sổ, nút, hình đại diện, phương tiện) có thời gian tồn tại tương đối lâu trong máy tính.

người trang trí

Ngôn ngữ Python cung cấp một cú pháp đơn giản nhưng mạnh mẽ được gọi là 'decorators'. Trình trang trí là một hàm hoặc một lớp bao bọc (hoặc trang trí) một hàm hoặc một phương thức. Hàm hoặc phương thức 'được trang trí' sẽ thay thế hàm hoặc phương thức 'không được trang trí' ban đầu. Bởi vì các hàm là đối tượng hạng nhất trong Python, điều này có thể được thực hiện 'thủ công', nhưng sử dụng cú pháp @decorator sẽ rõ ràng hơn và do đó được ưu tiên hơn

./sample/
6

Cơ chế này rất hữu ích để phân tách các mối quan tâm và tránh logic không liên quan bên ngoài 'làm ô nhiễm' logic cốt lõi của hàm hoặc phương thức. Một ví dụ điển hình về một phần chức năng được xử lý tốt hơn với trang trí là hoặc bộ nhớ đệm. bạn muốn lưu trữ kết quả của một hàm đắt tiền trong một bảng và sử dụng chúng trực tiếp thay vì tính toán lại chúng khi chúng đã được tính toán. Đây rõ ràng không phải là một phần của logic chức năng

Trình quản lý bối cảnh

Trình quản lý bối cảnh là một đối tượng Python cung cấp thêm thông tin theo ngữ cảnh cho một hành động. Thông tin bổ sung này có dạng chạy một hàm có thể gọi được khi bắt đầu ngữ cảnh bằng cách sử dụng câu lệnh

$ django-admin.py startproject samplesite
2, cũng như chạy một hàm có thể gọi được khi hoàn thành tất cả mã bên trong khối
$ django-admin.py startproject samplesite
2. Ví dụ nổi tiếng nhất về việc sử dụng trình quản lý ngữ cảnh được hiển thị ở đây, mở trên một tệp

./sample/
7

Bất kỳ ai quen thuộc với mẫu này đều biết rằng việc gọi

$ django-admin.py startproject samplesite
4 theo kiểu này đảm bảo rằng phương thức
$ django-admin.py startproject samplesite
6 của
$ django-admin.py startproject samplesite
5 sẽ được gọi vào một thời điểm nào đó. Điều này làm giảm tải nhận thức của nhà phát triển và làm cho mã dễ đọc hơn

Có hai cách dễ dàng để tự thực hiện chức năng này. sử dụng một lớp hoặc sử dụng một trình tạo. Hãy tự triển khai các chức năng trên, bắt đầu với cách tiếp cận lớp

./sample/
8

Đây chỉ là một đối tượng Python thông thường với hai phương thức bổ sung được sử dụng bởi câu lệnh

$ django-admin.py startproject samplesite
2. Đầu tiên, CustomOpen được khởi tạo và sau đó phương thức
$ django-admin.py startproject samplesite
8 của nó được gọi và bất kỳ giá trị trả về nào của
$ django-admin.py startproject samplesite
8 đều được gán cho
$ django-admin.py startproject samplesite
5 trong phần
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
1 của câu lệnh. Khi nội dung của khối
$ django-admin.py startproject samplesite
2 được thực thi xong, phương thức
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
3 sau đó được gọi

Và bây giờ, cách tiếp cận trình tạo bằng ngữ cảnh riêng của Python

./sample/
9

Điều này hoạt động theo cách chính xác giống như ví dụ về lớp ở trên, mặc dù nó ngắn gọn hơn. Hàm

README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
4 thực thi cho đến khi gặp câu lệnh
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
5. Sau đó, nó trao lại quyền kiểm soát cho câu lệnh
$ django-admin.py startproject samplesite
2, câu lệnh này gán bất kỳ thứ gì đã được ________5'ed cho f trong phần ____23_______1. Mệnh đề
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
9 đảm bảo rằng
./sample/
00 được gọi cho dù có ngoại lệ bên trong
$ django-admin.py startproject samplesite
2 hay không

Vì hai cách tiếp cận có vẻ giống nhau, chúng ta nên tuân theo Thiền của Python để quyết định khi nào nên sử dụng cách nào. Cách tiếp cận lớp có thể tốt hơn nếu có một lượng logic đáng kể để đóng gói. Cách tiếp cận chức năng có thể tốt hơn cho các tình huống mà chúng ta đang xử lý một hành động đơn giản

gõ động

Python được gõ động, có nghĩa là các biến không có kiểu cố định. Trên thực tế, trong Python, các biến rất khác so với các biến trong nhiều ngôn ngữ khác, cụ thể là các ngôn ngữ được nhập tĩnh. Các biến không phải là một phân đoạn của bộ nhớ máy tính nơi một số giá trị được ghi, chúng là 'thẻ' hoặc 'tên' trỏ đến các đối tượng. Do đó, biến 'a' có thể được đặt thành giá trị 1, sau đó là giá trị 'a string', thành hàm

Kiểu gõ động của Python thường được coi là một điểm yếu và thực sự nó có thể dẫn đến sự phức tạp và mã khó gỡ lỗi. Một cái gì đó có tên 'a' có thể được đặt thành nhiều thứ khác nhau và nhà phát triển hoặc người bảo trì cần theo dõi tên này trong mã để đảm bảo rằng nó không được đặt thành một đối tượng hoàn toàn không liên quan

Một số hướng dẫn giúp tránh vấn đề này

  • Tránh sử dụng cùng một tên biến cho những thứ khác nhau

Xấu

./sample.py
0

Tốt

./sample.py
1

Sử dụng các hàm hoặc phương thức ngắn giúp giảm nguy cơ sử dụng cùng tên cho hai thứ không liên quan

Tốt hơn là sử dụng các tên khác nhau ngay cả đối với những thứ có liên quan, khi chúng có một loại khác

Xấu

./sample.py
2

Không có hiệu quả đạt được khi sử dụng lại tên. các bài tập sẽ phải tạo các đối tượng mới. Tuy nhiên, khi độ phức tạp tăng lên và mỗi phép gán được phân tách bằng các dòng mã khác, bao gồm các nhánh và vòng lặp 'nếu', thì việc xác định loại biến đã cho trở nên khó khăn hơn.

Một số phương pháp mã hóa, chẳng hạn như lập trình chức năng, khuyến nghị không bao giờ gán lại một biến. Trong Java, điều này được thực hiện với từ khóa cuối cùng. Python không có từ khóa cuối cùng và dù sao nó cũng đi ngược lại triết lý của nó. Tuy nhiên, có thể là một kỷ luật tốt để tránh gán cho một biến nhiều lần và nó giúp nắm bắt được khái niệm về các loại có thể thay đổi và không thể thay đổi

Các loại có thể thay đổi và không thay đổi

Python có hai loại tích hợp hoặc do người dùng xác định

Các loại có thể thay đổi là những loại cho phép sửa đổi nội dung tại chỗ. Các biến điển hình là danh sách và từ điển. Tất cả các danh sách đều có các phương thức biến đổi, như

./sample/
02 hoặc
./sample/
03 và có thể được sửa đổi tại chỗ. Điều tương tự cũng xảy ra với từ điển

Các loại bất biến không cung cấp phương pháp thay đổi nội dung của chúng. Chẳng hạn, biến x được đặt thành số nguyên 6 không có phương thức “tăng”. Nếu bạn muốn tính x + 1, bạn phải tạo một số nguyên khác và đặt tên cho nó

./sample.py
3

Một hậu quả của sự khác biệt trong hành vi này là các loại có thể thay đổi không "ổn định" và do đó không thể được sử dụng làm khóa từ điển

Sử dụng các loại có thể thay đổi đúng cách cho những thứ có thể thay đổi về bản chất và các loại không thể thay đổi cho những thứ có bản chất cố định giúp làm rõ mục đích của mã

Ví dụ: tương đương bất biến của danh sách là bộ dữ liệu, được tạo bằng

./sample/
04. Bộ dữ liệu này là một cặp không thể thay đổi tại chỗ và có thể được sử dụng làm khóa cho từ điển

Một điểm đặc biệt của Python có thể khiến người mới bắt đầu ngạc nhiên là các chuỗi không thay đổi. Điều này có nghĩa là khi xây dựng một chuỗi từ các phần của nó, việc nối thêm từng phần vào chuỗi sẽ không hiệu quả vì toàn bộ chuỗi được sao chép trên mỗi phần nối thêm. Thay vào đó, sẽ hiệu quả hơn nhiều nếu tích lũy các phần trong một danh sách, có thể thay đổi được và sau đó dán (

./sample/
05) các phần lại với nhau khi cần chuỗi đầy đủ. Hiểu danh sách thường là cách nhanh nhất và thành ngữ nhất để làm điều này

Xấu

./sample.py
4

Tốt hơn

./sample.py
5

Tốt nhất

./sample.py
6

Một điều cuối cùng cần đề cập về chuỗi là sử dụng

./sample/
06 không phải lúc nào cũng tốt nhất. Trong trường hợp bạn đang tạo một chuỗi mới từ một số chuỗi được xác định trước, sử dụng toán tử cộng thực sự nhanh hơn. Nhưng trong những trường hợp như trên hoặc trong trường hợp bạn đang thêm vào một chuỗi hiện có, sử dụng
./sample/
06 sẽ là phương pháp ưa thích của bạn

./sample.py
7

Ghi chú

Bạn cũng có thể sử dụng toán tử định dạng để nối một số chuỗi xác định trước bên cạnh và

./sample/
09. Tuy nhiên, PEP 3101 không khuyến khích việc sử dụng toán tử
./sample/
10 để ủng hộ phương pháp