Đây có phải là một cách thực hành tốt hơn để khởi tạo trước các thuộc tính trong một lớp hay để thêm chúng trên đường đi?


12

Tôi xin lỗi nếu đây là một câu hỏi ngụy biện TUYỆT VỜI, nhưng tôi tò mò không biết những thực tiễn tốt nhất hiện có và dường như tôi không thể tìm thấy câu trả lời hay trên Google.

Trong Python, tôi thường sử dụng một lớp trống làm bộ chứa cấu trúc dữ liệu siêu bắt (giống như một tệp JSON) và thêm các thuộc tính trên đường đi:

class DataObj:
    "Catch-all data object"
    def __init__(self):
        pass

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

Điều này mang lại cho tôi một sự linh hoạt to lớn, bởi vì đối tượng container về cơ bản có thể lưu trữ bất cứ thứ gì. Vì vậy, nếu các yêu cầu mới tăng lên, tôi sẽ chỉ thêm nó dưới dạng một thuộc tính khác vào đối tượng DataObj (mà tôi chuyển xung quanh trong mã của mình).

Tuy nhiên, gần đây tôi rất ấn tượng (bởi các lập trình viên của FP) rằng đây là một thực tế khủng khiếp, bởi vì nó làm cho việc đọc mã rất khó khăn. Người ta phải trải qua tất cả các mã để tìm ra những thuộc tính mà DataObj thực sự có.

Câu hỏi : Làm thế nào tôi có thể viết lại điều này để duy trì tốt hơn mà không bị mất tính linh hoạt?

Có bất kỳ ý tưởng từ lập trình chức năng mà tôi có thể áp dụng?

Tôi đang tìm kiếm các thực hành tốt nhất ngoài đó.

Lưu ý : một ý tưởng là khởi tạo trước lớp với tất cả các thuộc tính mà người ta mong đợi gặp phải, vd

class DataObj:
    "Catch-all data object"
    def __init__(self):
        data.a = 0
        data.b = ""
        data.c = []

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

Đây thực sự là một ý tưởng tốt? Điều gì xảy ra nếu tôi không biết thuộc tính của mình là gì?


Cấu trúc dữ liệu của bạn rất dễ thay đổi đến nỗi bạn dường như quan tâm đến khả năng bảo trì của chúng. Trong thời gian rảnh rỗi ™, hãy thử đọc bài viết này về các mô hình dữ liệu bất biến . Nó có thể thay đổi hoàn toàn cách bạn suy luận về dữ liệu.
9000

@ 9000 Một bài viết như thế tái thuyết phục những người đã bị thuyết phục. Đối với tôi nó có vẻ giống như một cách làm hơn là tại sao (Danh sách các cá nhân không thực sự thuyết phục trừ khi bạn cảm thấy như bạn có những nhu cầu cụ thể đó). Đối với tôi, điều đó không thuyết phục được ai đó cập nhật hóa đơn trong VB rằng phải liên tục tạo các bản sao mới của đối tượng hóa đơn của họ (thêm thanh toán, đối tượng hóa đơn mới; thêm một phần, đối tượng hóa đơn mới).
Paul

Câu trả lời:


10

Làm thế nào tôi có thể viết lại điều này để duy trì tốt hơn mà không mất đi tính linh hoạt?

Bạn không. Sự linh hoạt chính xác là những gì gây ra vấn đề. Nếu bất kỳ mã nào ở bất cứ đâu có thể thay đổi những thuộc tính mà một đối tượng có, khả năng bảo trì đã ở dạng mảnh. Lý tưởng nhất, mỗi lớp có một tập các thuộc tính được đặt trong đá sau __init__và giống nhau cho mọi trường hợp. Không phải lúc nào cũng có thể hoặc hợp lý, nhưng nó nên xảy ra bất cứ khi nào bạn không có lý do thực sự tốt để tránh nó.

một ý tưởng là khởi tạo trước lớp với tất cả các thuộc tính mà người ta mong đợi gặp phải

Đó không phải là một ý tưởng tốt. Chắc chắn, sau đó thuộc tính là có, nhưng có thể có một giá trị không có thật, hoặc thậm chí là một giá trị hợp lệ bao gồm mã không gán giá trị (hoặc một lỗi chính tả). AttributeErrorlà đáng sợ, nhưng nhận được kết quả sai là tồi tệ hơn. Các giá trị mặc định nói chung là tốt, nhưng để chọn một mặc định hợp lý (và quyết định những gì được yêu cầu), bạn cần biết đối tượng được sử dụng để làm gì.

Điều gì xảy ra nếu tôi không biết thuộc tính của mình là gì?

Sau đó, bạn bị vặn trong mọi trường hợp và nên sử dụng một danh sách hoặc danh sách thay vì tên thuộc tính mã hóa cứng. Nhưng tôi hiểu ý bạn là "... tại thời điểm tôi viết lớp container". Sau đó, câu trả lời là: "Bạn có thể chỉnh sửa các tập tin trong lockstep, duh." Cần một thuộc tính mới? Thêm một thuộc tính frigging cho lớp container. Có nhiều mã sử dụng lớp đó và nó không cần thuộc tính đó? Cân nhắc chia nhỏ mọi thứ thành hai lớp riêng biệt (sử dụng mixin để duy trì DRY), vì vậy hãy làm cho nó tùy chọn nếu nó có ý nghĩa.

Nếu bạn sợ viết các lớp container lặp đi lặp lại: Áp dụng lập trình siêu dữ liệu một cách thận trọng hoặc sử dụng collections.namedtuplenếu bạn không cần phải thay đổi các thành viên sau khi tạo (bạn bè FP của bạn sẽ hài lòng).


7

Bạn luôn có thể sử dụng lớp Bunch của Alex Martelli . Trong trường hợp của bạn:

class DataObj:
    "Catch-all data object"
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

def processData(inputs):
    data = DataObj(a=1, b="sym", c=[2,5,2,1])

Bằng cách đó, ít nhất người đọc rõ ràng rằng dữ liệu chỉ là một kho lưu trữ dữ liệu câm và người ta có thể thấy ngay những giá trị nào được lưu trữ không phải là tên gì, vì tất cả xảy ra trong một dòng.

Và vâng, làm mọi thứ theo cách này thực tế là một ý tưởng tốt, đôi khi.


1

Tôi có thể sẽ sử dụng cách tiếp cận thứ hai, có thể sử dụng Noneđể chỉ ra dữ liệu không hợp lệ. Đúng là rất khó đọc / duy trì nếu bạn thêm thuộc tính sau này. Tuy nhiên, nhiều thông tin hơn về mục đích của lớp / đối tượng này sẽ cung cấp cái nhìn sâu sắc về lý do tại sao ý tưởng đầu tiên là một thiết kế tồi: bạn sẽ có một lớp hoàn toàn trống không có phương thức hoặc dữ liệu mặc định ở đâu? Tại sao bạn không biết lớp thuộc tính nào?

thể đó processDatalà một phương thức tốt hơn ( process_datađể tuân theo các quy ước đặt tên python), vì nó hoạt động theo lớp. Lấy ví dụ, có vẻ như nó có thể tốt hơn khi là một cấu trúc dữ liệu (trong đó dictcó thể đủ).

Đưa ra một ví dụ thực tế, bạn có thể xem xét đưa câu hỏi đến CodeReview , nơi họ có thể giúp cấu trúc lại mã.

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.