Làm cách nào để thiết kế một lớp trong Python?


143

Tôi đã có một số trợ giúp thực sự tuyệt vời cho các câu hỏi trước đây của tôi để phát hiện bàn chânngón chân trong một bàn chân , nhưng tất cả các giải pháp này chỉ hoạt động cho một phép đo tại một thời điểm.

Bây giờ tôi có dữ liệu bao gồm:

  • khoảng 30 con chó;
  • mỗi nhóm có 24 phép đo (được chia thành nhiều nhóm nhỏ);
  • mỗi phép đo có ít nhất 4 tiếp điểm (một cho mỗi chân) và
    • mỗi liên hệ được chia thành 5 phần và
    • có một vài thông số, như thời gian tiếp xúc, vị trí, tổng lực v.v.

văn bản thay thế

Rõ ràng việc gắn mọi thứ vào một đối tượng lớn sẽ không cắt được nó, vì vậy tôi nghĩ rằng tôi cần sử dụng các lớp thay vì hàng loạt chức năng hiện tại. Nhưng ngay cả khi tôi đã đọc chương Học về Python về các lớp, tôi không thể áp dụng nó vào mã của riêng mình ( liên kết GitHub )

Tôi cũng cảm thấy khá lạ khi xử lý tất cả dữ liệu mỗi khi tôi muốn lấy ra một số thông tin. Khi tôi biết vị trí của mỗi chân, không có lý do gì để tôi tính toán lại điều này. Hơn nữa, tôi muốn so sánh tất cả các bàn chân của cùng một con chó để xác định liên hệ nào thuộc về chân nào (trước / sau, trái / phải). Điều này sẽ trở thành một mớ hỗn độn nếu tôi tiếp tục chỉ sử dụng các chức năng.

Vì vậy, bây giờ tôi đang tìm kiếm lời khuyên về cách tạo các lớp sẽ cho phép tôi xử lý dữ liệu của mình ( liên kết đến dữ liệu được nén của một con chó ) theo cách hợp lý.


4
Bạn cũng có thể muốn xem xét sử dụng cơ sở dữ liệu (như sqlite: docs.python.org/l Library / sqlite3.html ). Bạn có thể viết một chương trình đọc các tệp dữ liệu khổng lồ của bạn và chuyển đổi nó thành các hàng trong các bảng cơ sở dữ liệu. Sau đó, ở giai đoạn thứ hai, bạn có thể viết các chương trình rút dữ liệu ra khỏi cơ sở dữ liệu để phân tích thêm.
unutbu

Ý bạn là gì đó như tôi đã hỏi ở đây @ubutbu? Tôi dự định làm cho nó làm điều đó, nhưng trước tiên tôi muốn có thể xử lý tất cả dữ liệu theo cách có tổ chức hơn
Ivo Flipse

Câu trả lời:


434

Cách thiết kế một lớp học.

  1. Viết những từ ngữ xuống. Bạn bắt đầu làm điều này. Một số người không và tự hỏi tại sao họ có vấn đề.

  2. Mở rộng tập hợp các từ của bạn thành các tuyên bố đơn giản về những gì các đối tượng này sẽ làm. Điều đó có nghĩa là, viết ra các tính toán khác nhau mà bạn sẽ thực hiện trên những điều này. Danh sách ngắn của bạn gồm 30 con chó, 24 phép đo, 4 liên hệ và một số "thông số" cho mỗi liên hệ là điều thú vị, nhưng chỉ là một phần của câu chuyện. "Vị trí của mỗi chân" và "so sánh tất cả các chân của cùng một con chó để xác định liên hệ nào thuộc về chân nào" là bước tiếp theo trong thiết kế đối tượng.

  3. Gạch chân các danh từ. Nghiêm túc. Một số người tranh luận về giá trị của điều này, nhưng tôi thấy rằng đối với các nhà phát triển OO lần đầu, nó giúp ích. Gạch chân các danh từ.

  4. Xem lại các danh từ. Các danh từ chung như "tham số" và "đo lường" cần được thay thế bằng các danh từ cụ thể, cụ thể áp dụng cho vấn đề của bạn trong miền vấn đề của bạn. Cụ thể giúp làm rõ vấn đề. Generics chỉ đơn giản là chi tiết elide.

  5. Đối với mỗi danh từ ("liên hệ", "paw", "dog", v.v.) hãy viết ra các thuộc tính của danh từ đó và các hành động mà đối tượng đó tham gia. Đừng cắt ngắn cái này. Mỗi thuộc tính. "Tập dữ liệu chứa 30 con chó" chẳng hạn rất quan trọng.

  6. Đối với mỗi thuộc tính, xác định xem đây có phải là mối quan hệ với một danh từ xác định hoặc một loại dữ liệu "nguyên thủy" hoặc "nguyên tử" khác như chuỗi hoặc dấu phẩy hoặc thứ gì đó không thể sửa chữa được.

  7. Đối với mỗi hành động hoặc hoạt động, bạn phải xác định danh từ nào có trách nhiệm và danh từ nào chỉ tham gia. Đó là một câu hỏi về "tính đột biến". Một số đối tượng được cập nhật, một số khác thì không. Các đối tượng có thể thay đổi phải sở hữu toàn bộ trách nhiệm đối với các đột biến của chúng.

  8. Tại thời điểm này, bạn có thể bắt đầu chuyển đổi danh từ thành định nghĩa lớp. Một số danh từ tập thể là danh sách, từ điển, bộ, bộ hoặc tên được đặt tên và bạn không cần phải làm nhiều việc. Các lớp khác phức tạp hơn, do dữ liệu dẫn xuất phức tạp hoặc do một số cập nhật / đột biến được thực hiện.

Đừng quên kiểm tra từng lớp một cách đơn giản bằng cách sử dụng unittest.

Ngoài ra, không có luật quy định các lớp phải có thể thay đổi. Trong trường hợp của bạn, ví dụ, bạn gần như không có dữ liệu có thể thay đổi. Những gì bạn có là dữ liệu dẫn xuất, được tạo bởi các hàm chuyển đổi từ bộ dữ liệu nguồn.


24

Các lời khuyên sau (tương tự như lời khuyên của @ S.Lott) là từ cuốn sách, Bắt đầu Python: Từ Novice đến Professional

  1. Viết một mô tả về vấn đề của bạn (vấn đề nên làm gì?). Gạch chân tất cả các danh từ, động từ và tính từ.

  2. Đi qua các danh từ, tìm kiếm các lớp tiềm năng.

  3. Đi qua các động từ, tìm kiếm các phương pháp tiềm năng.

  4. Đi qua các tính từ, tìm kiếm các thuộc tính tiềm năng

  5. Phân bổ các phương thức và thuộc tính cho các lớp của bạn

Để tinh chỉnh lớp học, cuốn sách cũng khuyên chúng ta có thể làm như sau:

  1. Viết xuống (hoặc mơ lên) một tập hợp các trường hợp sử dụng Cách thức chương trình của bạn có thể được sử dụng. Cố gắng bao gồm tất cả các chức năng.

  2. Hãy suy nghĩ từng bước sử dụng, đảm bảo rằng tất cả mọi thứ chúng ta cần đều được bảo hiểm.


Sẽ tốt hơn nếu có một số ví dụ về loại câu chúng ta phải viết.
endolith

14

Tôi thích cách tiếp cận TDD ... Vì vậy, hãy bắt đầu bằng cách viết các bài kiểm tra cho những gì bạn muốn hành vi. Và viết mã mà vượt qua. Tại thời điểm này, đừng quá lo lắng về thiết kế, chỉ cần có một bộ phần mềm thử nghiệm và phần mềm vượt qua. Đừng lo lắng nếu bạn kết thúc với một lớp lớn xấu xí duy nhất, với các phương thức phức tạp.

Đôi khi, trong quá trình ban đầu này, bạn sẽ tìm thấy một hành vi khó kiểm tra và cần được phân tách, chỉ để kiểm tra. Đây có thể là một gợi ý rằng một lớp riêng biệt được bảo hành.

Sau đó là phần thú vị ... tái cấu trúc. Sau khi bạn có phần mềm làm việc, bạn có thể thấy các phần phức tạp. Thường thì những hành vi nhỏ sẽ trở nên rõ ràng, gợi ý một lớp mới, nhưng nếu không, chỉ cần tìm cách để đơn giản hóa mã. Trích xuất các đối tượng dịch vụ và các đối tượng giá trị. Đơn giản hóa các phương pháp của bạn.

Nếu bạn đang sử dụng git đúng cách (bạn đang sử dụng git, phải không?), Bạn có thể nhanh chóng thử nghiệm một số phân tách cụ thể trong quá trình tái cấu trúc, sau đó từ bỏ nó và hoàn nguyên lại nếu nó không đơn giản hóa mọi thứ.

Bằng cách viết mã làm việc được thử nghiệm trước tiên, bạn sẽ hiểu rõ hơn về miền vấn đề mà bạn không thể dễ dàng có được với cách tiếp cận thiết kế đầu tiên. Viết bài kiểm tra và mã đẩy bạn qua tình trạng tê liệt "tôi bắt đầu từ đâu".


1
Tôi cũng đồng ý với câu trả lời này, mặc dù việc giải quyết vấn đề và xác định các lớp có thể (nghĩa là thực hiện kiến ​​trúc phần mềm "vừa đủ") có thể rất hữu ích nếu vấn đề sẽ được xử lý song song bởi một số thành viên trong nhóm.
Ben Smith

3

Toàn bộ ý tưởng của thiết kế OO là làm cho bản đồ mã của bạn trở thành vấn đề của bạn, vì vậy, ví dụ, khi bạn muốn bước chân đầu tiên của một con chó, bạn làm một việc như:

dog.footstep(0)

Bây giờ, có thể là đối với trường hợp của bạn, bạn cần đọc trong tệp dữ liệu thô của mình và tính toán các vị trí bước chân. Tất cả điều này có thể được ẩn trong hàm footstep () để nó chỉ xảy ra một lần. Cái gì đó như:

 class Dog:
   def __init__(self):
     self._footsteps=None 
   def footstep(self,n):
     if not self._footsteps:
        self.readInFootsteps(...)
     return self._footsteps[n]

[Đây là một kiểu mẫu lưu trữ. Lần đầu tiên nó đi và đọc dữ liệu bước chân, những lần tiếp theo nó chỉ lấy nó từ self._ feetsteps.]

Nhưng vâng, có được thiết kế OO đúng có thể là khó khăn. Nghĩ thêm về những điều bạn muốn làm với dữ liệu của mình và điều đó sẽ thông báo những phương pháp bạn sẽ cần áp dụng cho những lớp nào.


2

Viết ra các danh từ, động từ, tính từ của bạn là một cách tiếp cận tuyệt vời, nhưng tôi thích nghĩ về thiết kế lớp như đặt câu hỏi những dữ liệu nào nên được ẩn ?

Hãy tưởng tượng bạn có một Queryđối tượng và một Databaseđối tượng:

Đối Querytượng sẽ giúp bạn tạo và lưu trữ một truy vấn - lưu trữ, là chìa khóa ở đây, vì một chức năng có thể giúp bạn tạo một truy vấn dễ dàng như vậy. Có lẽ bạn có thể ở lại : Query().select('Country').from_table('User').where('Country == "Brazil"'). Nó không quan trọng chính xác cú pháp - đó là công việc của bạn! - chìa khóa là đối tượng đang giúp bạn ẩn thứ gì đó , trong trường hợp này là dữ liệu cần thiết để lưu trữ và xuất ra một truy vấn. Sức mạnh của đối tượng đến từ cú pháp sử dụng nó (trong trường hợp này là một chuỗi thông minh) và không cần biết những gì nó lưu trữ để làm cho nó hoạt động. Nếu được thực hiện đúng, Queryđối tượng có thể xuất các truy vấn cho nhiều hơn một cơ sở dữ liệu. Nó bên trong sẽ lưu trữ một định dạng cụ thể nhưng có thể dễ dàng chuyển đổi sang các định dạng khác khi xuất ra (Postgres, MySQL, MongoDB).

Bây giờ chúng ta hãy suy nghĩ thông qua các Databaseđối tượng. Điều này ẩn và lưu trữ những gì? Rõ ràng là nó không thể lưu trữ toàn bộ nội dung của cơ sở dữ liệu, vì đó là lý do tại sao chúng tôi có cơ sở dữ liệu! Vậy vấn đề là gì? Mục tiêu là để ẩn cách thức cơ sở dữ liệu hoạt động khỏi những người sử dụng Databaseđối tượng. Các lớp tốt sẽ đơn giản hóa lý luận khi thao tác trạng thái nội bộ. Đối với Databaseđối tượng này, bạn có thể ẩn cách các cuộc gọi mạng hoạt động, hoặc truy vấn hoặc cập nhật hàng loạt hoặc cung cấp một lớp bộ đệm.

Vấn đề là Databaseđối tượng này là LỚN. Nó đại diện cho cách truy cập cơ sở dữ liệu, vì vậy dưới vỏ bọc, nó có thể làm mọi thứ và mọi thứ. Rõ ràng việc kết nối mạng, bộ nhớ đệm và xử lý theo khối khá khó đối phó tùy thuộc vào hệ thống của bạn, vì vậy việc ẩn chúng đi sẽ rất hữu ích. Nhưng, như nhiều người sẽ lưu ý, một cơ sở dữ liệu cực kỳ phức tạp và càng xa các cuộc gọi DB thô mà bạn nhận được, càng khó điều chỉnh hiệu năng và hiểu cách mọi thứ hoạt động.

Đây là sự đánh đổi cơ bản của OOP. Nếu bạn chọn sự trừu tượng đúng, nó làm cho việc mã hóa trở nên đơn giản hơn (Chuỗi, Mảng, Từ điển), nếu bạn chọn một sự trừu tượng quá lớn (Cơ sở dữ liệu, Trình quản lý email, Trình quản lý mạng), nó có thể trở nên quá phức tạp để thực sự hiểu cách thức hoạt động của nó hoặc làm gì chờ đợi. Mục tiêu là để che giấu sự phức tạp , nhưng một số phức tạp là cần thiết. Một nguyên tắc nhỏ là bắt đầu tránh Managercác đối tượng, và thay vào đó tạo các lớp giống như structs- tất cả những gì họ làm là giữ dữ liệu, với một số phương thức trợ giúp để tạo / thao tác dữ liệu để giúp cuộc sống của bạn dễ dàng hơn. Ví dụ, trong trường hợp EmailManagerbắt đầu với một hàm được gọi là sendEmaillấy một Emailđối tượng. Đây là một điểm khởi đầu đơn giản và mã rất dễ hiểu.

Ví dụ của bạn, hãy nghĩ về những gì dữ liệu cần phải được cùng nhau để tính toán những gì bạn đang tìm kiếm. Ví dụ, nếu bạn muốn biết một con vật đi được bao xa, bạn có thể có AnimalStepAnimalTrip(các bộ sưu tập các lớp AnimalSteps). Bây giờ, mỗi Chuyến đi có tất cả dữ liệu Bước, sau đó nó sẽ có thể tìm ra thứ gì đó về nó, có lẽ có AnimalTrip.calculateDistance()ý nghĩa.


2

Sau khi lướt qua mã được liên kết của bạn, có vẻ như tôi tốt hơn hết là không thiết kế lớp Dog vào thời điểm này. Thay vào đó, bạn nên sử dụng Pandasdataframes . Một dataframe là một bảng có các cột. Bạn dataframe sẽ có các cột như: dog_id, contact_part, contact_time, contact_location,, vv Pandas sử dụng NumPy mảng đằng sau hậu trường, và nó có nhiều phương pháp thuận tiện cho bạn:

  • Chọn một con chó bằng cách: my_measurements['dog_id']=='Charly'
  • Lưu dữ liệu: my_measurements.save('filename.pickle')
  • Cân nhắc sử dụng pandas.read_csv()thay vì đọc thủ công các tệp văn bản.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.