Hướng dẫn python project structure example - ví dụ cấu trúc dự án python

Hướng dẫn python project structure example - ví dụ cấu trúc dự án python

Theo cấu trúc của người Viking, chúng tôi 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ã sạch, hiệu quả. Về mặt thực tế, cấu trúc của người Viking có nghĩa là tạo mã sạch có logic và 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 tin.

Những chức năng nào nên đi vào mô -đun nào? Làm thế nào để dữ liệu chảy qua dự án? Những tính năng và chức năng có thể được nhóm lại với nhau và cô lập? Bằng cách trả lời các câu hỏi như thế này, bạn có thể bắt đầu lập kế hoạch, theo nghĩa rộng, sản phẩm hoàn chỉnh của bạn sẽ trông như thế nào.

Trong phần này, chúng tôi xem xét kỹ hơn các mô -đun và hệ thống nhập khẩu Python, 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ữ Or

Nó quan trọng.¶

Cũng giống như phong cách 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 người dùng hoặc người đóng góp tiềm năng hạ cánh trên trang kho lưu trữ của bạn, họ sẽ thấy một vài điều:

  • Tên dự án
  • mô tả dự án
  • Bunch o tệp

Chỉ khi họ cuộn bên dưới nếp gấp, người dùng mới thấy dự án của bạn README.

Nếu repo của bạn là một kết xuất lớn các tập tin hoặc một mớ hỗn độn của các thư mục, họ có thể tìm kiếm ở nơi khác trước khi đọc tài liệu đẹp của bạ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ả mọi thứ. Bạn và các đồng nghiệp của bạn sẽ dành vô số thời gian làm việc với kho lưu trữ này, cuối cùng 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.: This is what Kenneth Reitz recommended in 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 để vào một số chi tiết cụ thể.

Mô -đun thực tế

Địa điểm
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 hoặc
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
7
Mụ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 bị bỏ đi:

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 gốc của kho lưu trữ của bạn:

Thư viện của bạn không thuộc về SRC mơ hồ hoặc thư mục con Python.

Giấy phép¶

Địa điểm
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 hoặc
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
7
Mụ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 bị bỏ đi:

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 gốc của kho lưu trữ của bạn:

Thư viện của bạn không thuộc về SRC mơ hồ hoặc thư mục con Python.

Giấy phép¶

Địa điểm
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 hoặc
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
7
Mụ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 bị bỏ đi:

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 gốc của kho lưu trữ của bạn:

Địa điểm
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 hoặc
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
7
Mụ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 bị bỏ đi:

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 gốc của kho lưu trữ của bạn:

Thư viện của bạn không thuộc về SRC mơ hồ hoặc thư mục con Python.

Địa điểm
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 hoặc
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
7
Mụ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 bị bỏ đi:

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 gốc của kho lưu trữ của bạn:

Thư viện của bạn không thuộc về SRC mơ hồ hoặc thư mục con Python.Testing Your Code.

Địa điểm
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
6 hoặc
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample
7
Mụ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 bị bỏ đi:

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 gốc của kho lưu trữ của bạn:

tests/test_basic.py
tests/test_advanced.py

Thư viện của bạn không thuộc về SRC mơ hồ hoặc thư mục con Python.

  • Giấy phép¶
  • import os
    import sys
    sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
    
    import sample
    
    8

Lập pháp lên.

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

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

import sample

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

from .context import sample

Tất nhiên, bạn cũng được tự do xuất bản mã mà không có 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 vào mã của bạn.

Setup.py¶

Makefile¶

Địa điểm
from .context import sample
7
Mục đíchNhiệm vụ quản lý chung.

Nếu bạn nhìn vào 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? Tóm lại, các dự án này được viết bằng C C C, Make là một công cụ cực kỳ hữu ích để xác định các nhiệm vụ chung cho dự án của bạn.

Mẫu Makefile:

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 (ví dụ:

from .context import sample
8 hoặc
from .context import sample
9) cũng thuộc về gốc của kho lưu trữ.

Liên quan đến các ứ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ữ kém do các mẫu ứng dụng mới đi kèm.

Làm sao? Chà, họ đi đến kho lưu trữ trần và mới của họ và chạy như sau, như họ luôn có:

$ 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

Don lồ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. Nesting không cần thiết không giúp ích cho bất cứ ai (trừ khi họ hoài cổ cho các repos SVN nguyên khối).

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

$ django-admin.py startproject samplesite .

Lưu ý các

init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
0.

Cấu trúc kết quả:

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

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

Nhờ cách nhập khẩu 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 còn lại với nhiệm vụ kiến ​​trúc thuần túy là tạo ra các phần khác nhau trong dự án và các tương tác của họ.

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

  • Nhiều phụ thuộc tròn lộn xộn: Nếu bảng và ghế trong bảng trong
    init:
        pip install -r requirements.txt
    
    test:
        py.test tests
    
    .PHONY: init test
    
    1 cần nhập thợ mộc từ
    init:
        pip install -r requirements.txt
    
    test:
        py.test tests
    
    .PHONY: init test
    
    2 để trả lời một câu hỏi như
    init:
        pip install -r requirements.txt
    
    test:
        py.test tests
    
    .PHONY: init test
    
    3, và nếu ngược lại, người thợ mộc lớp cần nhập bảng và chủ tịch để trả lời câu hỏi
    init:
        pip install -r requirements.txt
    
    test:
        py.test tests
    
    .PHONY: init test
    
    4, thì bạn có một phụ thuộc tròn. Trong trường hợp này, bạn sẽ phải dùng đến các hack mỏng manh như sử dụng các câu lệnh nhập trong các phương thức hoặc chức năng của bạn.
  • Khớp nối ẩn: Mỗi và mọi thay đổi trong việc thực hiện bảng phá vỡ 20 thử nghiệm trong các trường hợp thử nghiệm không liên quan vì nó phá vỡ mã Carpenter, đòi hỏi phải phẫu thuật rất cẩn thận để thích nghi với sự thay đổi. Điều này có nghĩa là bạn có quá nhiều giả định về bảng trong mã thợ mộc hoặc ngược lại.
  • Việc sử dụng nặng trạng thái hoặc bối cảnh toàn cầu: Thay vì chuyển
    init:
        pip install -r requirements.txt
    
    test:
        py.test tests
    
    .PHONY: init test
    
    5 một cách rõ ràng cho nhau, bảng và thợ mộc dựa vào các biến toàn cầu có thể được sửa đổi và được sửa đổi trên đường bay bởi các tác nhân khác nhau. Bạn cần xem xét kỹ lưỡng tất cả các quyền truy cập vào các biến toàn cầu này để hiểu lý do tại sao một bảng hình chữ nhật 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 bối cảnh này, gây rối với kích thước bảng.
  • Mã spaghetti: Nhiều trang lồng nhau nếu mệnh đề và cho các vòng lặp có 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ó) làm cho nó rất khó để duy trì loại mã này. Tin tốt là bạn có thể không thấy quá nhiều về nó.
  • Mã ravioli có nhiều khả năng trong Python: nó bao gồm hàng trăm mảnh logic nhỏ tương tự, thường là các lớp hoặc đối tượng, không có cấu trúc thích 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í là Tablenew cho nhiệm vụ của mình, thì bạn có thể đang bơi theo mã ravioli.

Mô -đun bình

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à loại 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 giữ 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 các hành động của người dùng, trong khi một lớp khác sẽ xử lý thao tác dữ liệu cấp 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 thoa 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 tuyên bố

init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
6 và
init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
7.

Ngay khi bạn sử dụng các 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 như HĐH và SYS, các mô-đun bên thứ ba bạn đã cài đặt trong môi trường của bạn hoặc các mô-đun nội bộ dự án của bạn.

Để xếp hàng với hướng dẫn kiểu, hãy giữ tên mô -đun ngắn, chữ thường và chắc chắn tránh sử dụng các ký hiệu đặc biệt như dấu chấm (.) Hoặc dấu hỏi (?). Một tên tệp như

init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
8 là tên bạn nên tránh! Đặt tên theo cách này sẽ can thiệp vào cách Python tìm kiếm các mô -đun.

Trong trường hợp của My.spam.py Python dự kiến ​​sẽ tìm thấy một tệp

init:
    pip install -r requirements.txt

test:
    py.test tests

.PHONY: init test
9 trong một thư mục có tên
$ django-admin.py startproject samplesite
0 không phải là trường hợp. Có một ví dụ về cách sử dụng ký hiệu dấu chấm trong các tài liệu Python.

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

$ django-admin.py startproject samplesite
1, nhưng ngay cả người bạn đáng tin cậy của chúng tôi cũng không nên nhìn thấy rằng trong tên mô -đun. Tuy nhiên, sử dụng các ký tự khác (khoảng trắng hoặc dấu gạch nối) trong tên mô-đun sẽ ngăn chặn 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à, hầu hết tất cả, không gian tên don don với dấu gạch dưới; sử dụng các mô hình con thay thế.

# OK
import library.plugin.foo
# not OK
import library.foo_plugin

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

Một cách cụ thể, câu lệnh

$ django-admin.py startproject samplesite
2 sẽ tìm kiếm tệp thích hợp, đó là
$ django-admin.py startproject samplesite
3 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
$ django-admin.py startproject samplesite
3 trong đường dẫn đường dẫn đệ quy và nâng một ngoại lệ nhập khẩu khi nó không được tìm thấy.

Khi

$ django-admin.py startproject samplesite
3 được tìm thấy, trình thông dịch Python sẽ thực thi mô -đun trong một phạm vi bị cô lập. Bất kỳ tuyên bố cấp cao nhất trong
$ django-admin.py startproject samplesite
3 sẽ được thực thi, bao gồm cả các nhập khẩu khác nếu có. Các định nghĩa chức năng và lớp được lưu trữ trong từ điển mô -đun.

Sau đó, các biến, chức năng và các lớp của mô -đun sẽ có sẵn cho người gọi thông qua không gian tên 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 chỉ thị

$ django-admin.py startproject samplesite
7 được trình tiền xử lý sử dụng để lấy tất cả các mã được tìm thấy trong tệp và ‘sao chép nó vào mã người gọi. Nó khác nhau trong Python: mã được bao gồm được phân lập trong 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ã bao gồm có thể có các hiệu ứng không mong muốn, ví dụ: Ghi đè một chức năng hiện có với 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:

$ django-admin.py startproject samplesite
8. Điều này thường được coi là thực hành xấu. Sử dụng
$ django-admin.py startproject samplesite
9 làm cho mã khó đọc hơn và làm cho các phụ thuộc ít ngăn cách hơn.Using
$ django-admin.py startproject samplesite
9 makes the code harder to read and makes dependencies less compartmentalized.

Sử dụng

README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
0 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
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
1 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 lợi thế duy nhất của nó so với
$ django-admin.py startproject samplesite
2 đơn giản hơn là nó sẽ tiết kiệm một chút gõ.

Rất tệ

tests/test_basic.py
tests/test_advanced.py
0

Tốt hơn

tests/test_basic.py
tests/test_advanced.py
1

Tốt nhất

tests/test_basic.py
tests/test_advanced.py
2

Như đã đề cập trong phần Kiểu mã, 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 và lộn xộn vô dụng; Do đó, một số nỗ lực được dành để cố gắng đạt được một mức độ ngắn gọn nhất định. Nhưng sự khó chịu và tối nghĩa là những giới hạn mà sự ngắn gọn nên dừng lại. Có thể cho 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ữ

README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
3, cải thiện đáng kể khả năng đọc mã và khả năng hiểu trong tất cả các dự án tệp đơn giản nhất.Code Style section, readability is one of the main features of Python. Readability means to avoid useless boilerplate text and clutter; therefore some efforts are spent trying to achieve a certain level of brevity. But terseness and obscurity are the limits where brevity should stop. Being able to tell immediately where a class or function comes from, as in the
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
3 idiom, greatly improves code readability and understandability in all but the simplest single file projects.

Gói

Python cung cấp một hệ thống bao bì rất đơn giả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

README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
4 đề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 cho tệp
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
4, được sử dụng để thu thập tất cả các định nghĩa trên toàn gói.

Một tệp

$ django-admin.py startproject samplesite
3 trong thư mục
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
7 được nhập với câu lệnh
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
8. Tuyên bố này sẽ tìm tệp
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
4 trong
$ django-admin.py startproject samplesite .
0 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 kiếm một tệp có tên
$ django-admin.py startproject samplesite .
1 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 hoạt động này, bất kỳ biến, chức năng hoặc lớp nào được xác định trong
$ django-admin.py startproject samplesite
3 đều có sẵn trong không gian tên Pack.Modu.

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

README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
4. Khi độ phức tạp của dự án phát triển, có thể có các gói phụ và gói phụ trong một cấu trúc thư mục sâu. Trong trường hợp này, việc nhập một mục duy nhất từ ​​gói phụ sẽ yêu cầu thực hiện tất cả các tệp
README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
4 đã được đáp ứng trong khi đi qua cây.

Để lại một tệp

README.rst
samplesite/manage.py
samplesite/samplesite/settings.py
samplesite/samplesite/wsgi.py
samplesite/samplesite/sampleapp/models.py
4 trống được coi là bình thường và thậm chí là thực hành tốt, nếu các mô-đun và gói phụ của gói không cần phải chia sẻ bất kỳ mã nào.

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

$ django-admin.py startproject samplesite .
6. Điều này cho phép bạn sử dụng mod thay cho sự lặp lại của dòng chảy của
$ django-admin.py startproject samplesite .
7.

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ể hơi sai lệch và đòi hỏi phải 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à những gì có nghĩa là khi chúng ta nói, ví dụ, các chức năng là các đối tượng hạng nhất. Các chức năng, lớp, chuỗi và thậm chí các loại là đố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ố chức năng và chúng có thể có các phương thức và thuộc tính. Trong sự hiểu biết 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ô hình lập trình chính. Nó hoàn toàn khả thi cho một dự án Python không được định hướng đối tượng, tức là không sử dụng hoặc 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 các ngôn ngữ lập trình hướng đối tượng.

Hơn nữa, như đã thấy trong phần mô-đun, 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 đóng gói và 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 định hướng đối tượng. Do đó, các lập trình viên Python có nhiều vĩ độ hơn là không sử dụng định 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 định hướng đối tượng không cần thiết. Xác định các lớp tùy chỉnh là hữu ích khi chúng ta muốn dán một số trạng thái và một số chức năng cùng nhau. Vấn đề, như được chỉ ra bởi 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, thông thường các ứng dụng web, nhiều trường hợp 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 khởi tạo, có nghĩa là giữ một số thông tin tĩnh về thế giới, dễ bị các vấn đề đồng thời 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 bằng phương pháp

$ django-admin.py startproject samplesite .
8) và việc sử dụng trạng thái đối tượng thực tế 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 giữ lại có thể bị 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 một lúc, việc xóa thực sự có thể xảy ra sau khi quá trình đầu tiên tải vật phẩm, và sau đó chúng ta phải đánh dấu một đối tượng bị xóa khi đọc.

Điều này và các vấn đề khác đã dẫn đến ý tưởng rằng sử dụng các chức năng 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 chức năng và quy trình với càng ít bối cảnh ngầm và tác dụng phụ càng tốt. Chức năng Bối cảnh ngầm được tạo thành từ bất kỳ biến hoặc mục toàn cầu nào trong lớp tồn tại đượ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 trong bối cảnh ngầm của nó. Nếu một hàm lưu hoặc xóa dữ liệu trong một biến toàn cầu hoặc trong lớp tồn tại, nó được cho là có tác dụng phụ.

Cẩn thận cách ly các chức năng với bối cảnh và tác dụng phụ với các chức năng với logic (được gọi là các hàm thuần túy) cho phép các lợi ích sau:

  • Các hàm thuần túy là xác định: Cho đầu vào cố định, đầu ra sẽ luôn giống nhau.
  • Các chức năng thuần túy dễ dàng hơn nhiều để thay đổi hoặc thay thế nếu chúng cần được tái cấu trúc hoặc tối ưu hóa.
  • Các chức năng thuần túy dễ dàng hơn để kiểm tra 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ễ dàng hơn để thao tác, trang trí và vượt qua.

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

Rõ ràng, định 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ụ khi phát triển các ứng dụng hoặc trò chơi máy tính để bàn đồ họa, trong đó những thứ được thao tác (Windows, Nút, Avatars, phương tiện) có một cuộc sống tương đối dài của riêng họ trong máy tính kỉ niệm.

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à ‘trang trí. Một người trang trí là một chức năng hoặc một lớp kết thúc (hoặc trang trí) một chức năng hoặc một phương pháp. Hàm hoặc phương pháp được trang trí trên mạng sẽ thay thế chức năng hoặc phương pháp không được trang trí ban đầu. Bởi vì các chức năng là các đối tượng hạng nhất trong Python, điều này có thể được thực hiện bằng tay, nhưng sử dụng cú pháp @Decorator rõ ràng hơn và do đó được ưa thích.

tests/test_basic.py
tests/test_advanced.py
3

Cơ chế này rất hữu ích để tách các mối quan tâm và tránh logic không liên quan bên ngoài ‘gây ô nhiễm logic logic cốt lõi của hàm hoặc phương pháp. 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à ghi nhớ hoặc lưu trữ: bạn muốn lưu trữ kết quả của một chức năng đắt tiền trong 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.

Người 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ông tin theo ngữ cảnh bổ sung cho một hành động. Thông tin bổ sung này có hình thức chạy một cuộc gọi khi bắt đầu ngữ cảnh bằng cách sử dụng câu lệnh

$ django-admin.py startproject samplesite .
9, cũng như chạy một cuộc gọi có thể gọi được khi hoàn thành tất cả các mã bên trong khối
$ django-admin.py startproject samplesite .
9. 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:

tests/test_basic.py
tests/test_advanced.py
4

Bất cứ ai quen thuộc với mô hình này đều biết rằng việc gọi

README.rst
manage.py
samplesite/settings.py
samplesite/wsgi.py
samplesite/sampleapp/models.py
1 trong thời trang này đảm bảo rằng phương pháp ____ 82 ____ ____ ____83 sẽ được gọi tại một số điểm. Đ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 trình tạo. Hãy để tự mình thực hiện các chức năng trên, bắt đầu với phương pháp lớp học:

tests/test_basic.py
tests/test_advanced.py
5

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

$ django-admin.py startproject samplesite .
9. CustomOpen được khởi tạo đầu tiên và sau đó phương thức
README.rst
manage.py
samplesite/settings.py
samplesite/wsgi.py
samplesite/sampleapp/models.py
5 của nó được gọi và bất cứ điều gì
README.rst
manage.py
samplesite/settings.py
samplesite/wsgi.py
samplesite/sampleapp/models.py
5 trả về được gán cho
README.rst
manage.py
samplesite/settings.py
samplesite/wsgi.py
samplesite/sampleapp/models.py
2 trong phần
README.rst
manage.py
samplesite/settings.py
samplesite/wsgi.py
samplesite/sampleapp/models.py
8 của câu lệnh. Khi nội dung của khối
$ django-admin.py startproject samplesite .
9 được thực hiện xong, phương thức
# OK
import library.plugin.foo
# not OK
import library.foo_plugin
0 sau đó được gọi.

Và bây giờ cách tiếp cận của Trình tạo bằng cách sử dụng bối cảnh riêng của Python:

tests/test_basic.py
tests/test_advanced.py
6

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

# OK
import library.plugin.foo
# not OK
import library.foo_plugin
1 thực thi cho đến khi nó đạt đến câu lệnh
# OK
import library.plugin.foo
# not OK
import library.foo_plugin
2. Sau đó, nó cung cấp quyền kiểm soát trở lại câu lệnh
$ django-admin.py startproject samplesite .
9, trong đó gán bất cứ điều gì được ____ 92 đã được chuyển cho F trong phần
README.rst
manage.py
samplesite/settings.py
samplesite/wsgi.py
samplesite/sampleapp/models.py
8. Điều khoản
# OK
import library.plugin.foo
# not OK
import library.foo_plugin
6 đảm bảo rằng
# OK
import library.plugin.foo
# not OK
import library.foo_plugin
7 được gọi là liệu có ngoại lệ bên trong
$ django-admin.py startproject samplesite .
9 hay không.

Vì hai cách tiếp cận xuất hiện giống nhau, chúng ta nên tuân theo Zen of Python để quyết định khi nào nên sử dụng. Cách tiếp cận của lớp có thể tốt hơn nếu có một lượng logic đáng kể để gói gọn. 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 tôi xử lý một hành động đơn giản.

Gõ động Dynamic

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

Việc 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ó bỏ qua. 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 nó chưa đượ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

tests/test_basic.py
tests/test_advanced.py
7

Tốt

tests/test_basic.py
tests/test_advanced.py
8

Sử dụng các chức năng hoặc phương pháp ngắn giúp giảm nguy cơ sử dụng cùng tên cho hai điều 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

tests/test_basic.py
tests/test_advanced.py
9

Tốt

Sử dụng các chức năng hoặc phương pháp ngắn giúp giảm nguy cơ sử dụng cùng tên cho hai điều 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:

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 phát triển và mỗi bài tập được phân tách bằng các dòng mã khác, bao gồm ‘nếu các nhánh và vòng lặp, thì việc xác định loại biến nhất định là khó hơn.

Một số thực tiễn mã hóa, như lập trình chức năng, đề nghị không bao giờ phân công 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 sẽ chống lại triết lý của nó. Tuy nhiên, nó có thể là một kỷ luật tốt để tránh gán cho một biến nhiều hơn một lần, và nó giúp nắm bắt khái niệm về các loại có thể thay đổi và bất biến.

Các loại có thể thay đổi và bất biến

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

import sample
0

Python có hai loại 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 thiết bị tiêu biểu là danh sách và từ điển: Tất cả các danh sách có các phương thức đột biến, như

# OK
import library.plugin.foo
# not OK
import library.foo_plugin
9 hoặc
tests/test_basic.py
tests/test_advanced.py
00 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 họ. Chẳng hạn, biến X được đặt thành số nguyên 6 không có phương thức tăng tăng. Nếu bạn muốn tính toán x + 1, bạn phải tạo một số nguyên khác và đặt tên cho nó.

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

Xấu

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

import sample
1

Tốt hơn

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

import sample
2

Tốt nhất

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

import sample
3

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

tests/test_basic.py
tests/test_advanced.py
03 không phải lúc nào cũng tốt nhất.Trong các 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ử bổ sung thực sự nhanh hơn.Nhưng trong các 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
tests/test_basic.py
tests/test_advanced.py
03 nên là phương pháp ưa thích của bạn.

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

import sample
4

Ghi chú

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

tests/test_basic.py
tests/test_advanced.py
05 và
tests/test_basic.py
tests/test_advanced.py
06.Tuy nhiên, PEP 3101 không khuyến khích việc sử dụng toán tử
tests/test_basic.py
tests/test_advanced.py
07 có lợi cho phương pháp
tests/test_basic.py
tests/test_advanced.py
08.% formatting operator to concatenate a pre-determined number of strings besides
tests/test_basic.py
tests/test_advanced.py
05 and
tests/test_basic.py
tests/test_advanced.py
06. However, PEP 3101 discourages the usage of the
tests/test_basic.py
tests/test_advanced.py
07 operator in favor of the
tests/test_basic.py
tests/test_advanced.py
08 method.

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

import sample
5

Phụ thuộc bán hàng hóa

Người chạy bộ