Làm thế nào để cấu trúc lại một lớp thần Python Python?


10

Vấn đề

Tôi đang làm việc với một dự án Python có lớp chính là một chút God God Đối tượng . Có nên friggin' nhiều thuộc tính và phương pháp!

Tôi muốn cấu trúc lại lớp.

Cho đến nay…

Bước đầu tiên, tôi muốn làm một cái gì đó tương đối đơn giản; nhưng khi tôi thử cách tiếp cận đơn giản nhất, nó đã phá vỡ một số thử nghiệm và các ví dụ hiện có.

Về cơ bản, lớp này có một danh sách các thuộc tính loooong nhưng tôi có thể nhìn rõ chúng và nghĩ rằng, 5 thuộc tính này có liên quan với nhau. 8 cái này cũng có liên quan

nhận được

Về cơ bản tôi chỉ muốn nhóm các thuộc tính liên quan thành một lớp người trợ giúp giống như chính tả. Tôi đã có một cảm giác __getattr__sẽ là lý tưởng cho công việc. Vì vậy, tôi đã chuyển các thuộc tính sang một lớp riêng biệt, và, chắc chắn, __getattr__hoạt động phép thuật của nó hoàn toàn tốt

Lúc đầu .

Nhưng sau đó tôi đã thử chạy một trong những ví dụ. Lớp con ví dụ cố gắng đặt một trong các thuộc tính này trực tiếp (ở cấp độ lớp ). Nhưng vì thuộc tính này không còn nằm trên cơ sở vật lý, trong lớp cha, tôi đã gặp lỗi khi nói rằng thuộc tính này không tồn tại.

@bất động sản

Sau đó tôi đọc lên về @propertytrang trí. Nhưng sau đó tôi cũng đọc rằng nó tạo ra các vấn đề cho các lớp con muốn thực hiện self.x = blahkhi xlà một thuộc tính của lớp cha.

Mong muốn

  • Có tất cả mã khách hàng tiếp tục hoạt động bằng cách sử dụng self.whatever, ngay cả khi whatevertài sản của cha mẹ không phải là xác định vị trí vật lý trong chính lớp (hoặc ví dụ).
  • Nhóm thuộc tính liên quan vào các thùng chứa giống như dict.
  • Giảm sự ồn ào cực độ của mã trong lớp chính.

Ví dụ, tôi không chỉ đơn giản muốn thay đổi điều này:

larry = 2
curly = 'abcd'
moe   = self.doh()

Vào đây:

larry = something_else('larry')
curly = something_else('curly')
moe   = yet_another_thing.moe()

Vì thế mà vẫn ồn ào. Mặc dù điều đó thành công biến một thuộc tính đơn giản thành thứ gì đó có thể quản lý dữ liệu, bản gốc có 3 biến và phiên bản được điều chỉnh vẫn có 3 biến.

Tuy nhiên, tôi sẽ ổn với thứ như thế này:

stooges = Stooges()

Và nếu tìm kiếm self.larrythất bại, một cái gì đó sẽ kiểm tra stoogesvà xem nếu larrycó. (Nhưng nó cũng phải hoạt động nếu một lớp con cố gắng thực hiện larry = 'blah'ở cấp lớp.)

Tóm lược

  • Muốn thay thế các nhóm thuộc tính có liên quan trong lớp cha bằng một thuộc tính duy nhất lưu trữ tất cả dữ liệu ở nơi khác
  • Muốn làm việc với mã máy khách hiện có sử dụng (ví dụ) larry = 'blah'ở cấp độ lớp
  • Muốn tiếp tục cho phép các lớp con mở rộng, ghi đè và sửa đổi các thuộc tính được cấu trúc lại này mà không biết bất cứ điều gì đã thay đổi


Điều này có thể không? Hay tôi đang sủa sai cây?


6
Bạn đang thiếu một nửa lợi ích nếu bạn khăng khăng vẫn có giao diện giống như thần khổng lồ này, ngay cả khi bạn tách rời các phần của việc thực hiện. Bạn có thể cung cấp các phím tắt, nhưng chỉ cần đặt các biến vào các không gian tên khác nhau và chuyển hướng hoàn toàn đến các biến đó mang lại cho bạn rất ít, nếu có bất cứ điều gì.

1
@delnan: Được rồi, vậy bạn muốn giới thiệu cái gì?
Zearin

Câu trả lời:


9

Sau khi viết và tái cấu trúc một "con vật Chúa" trăn, tôi thông cảm. Những gì tôi đã làm là chia đối tượng ban đầu thành các phần phụ dựa trên các phương thức. Chẳng hạn, bản gốc trông giống mã giả này:

method A():
    self.bla += 1

method B():
    self.bla += 1

do stuff():
    self.bla = 1
    method A()
    method B()
    print self.bla

Phương pháp công cụ là một "đơn vị" công việc khép kín. Tôi đã di chuyển nó ra một lớp mới mà bản gốc bắt đầu. Điều này kéo ra các thuộc tính cần thiết là tốt. Một số chỉ được sử dụng bởi lớp phụ và có thể di chuyển thẳng. Những người khác đã được chia sẻ, và đã được chuyển vào một lớp chia sẻ.

"Đối tượng Chúa" tạo ra một bản sao mới của lớp chia sẻ khi bắt đầu và mỗi lớp con mới chấp nhận một con trỏ như là một phần của phương thức init của chúng. Ví dụ: đây là phiên bản bị tước của người gửi thư:

#!/usr/bin/env python
# -*- coding: ascii -*-
'''Functions for emailing with dirMon.'''

from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
import os
import smtplib
import datetime
import logging

class mailer:
    def __init__(self,SERVER="mail.server.com",FROM="support@server.com"):
        self.server = SERVER
        self.send_from = FROM
        self.logger = logging.getLogger('dirMon.mailer')

    def send_mail(self, send_to, subject, text, files=[]):
        assert type(send_to)==list
        assert type(files)==list
        if self.logger.isEnabledFor(logging.DEBUG):
            self.logger.debug(' '.join(("Sending email to:",' '.join(send_to))))
            self.logger.debug(' '.join(("Subject:",subject)))
            self.logger.debug(' '.join(("Text:",text)))
            self.logger.debug(' '.join(("Files:",' '.join(files))))
        msg = MIMEMultipart()
        msg['From'] = self.send_from
        msg['To'] = COMMASPACE.join(send_to)
        msg['Date'] = formatdate(localtime=True)
        msg['Subject'] = subject
        msg.attach( MIMEText(text) )
        for f in files:
            part = MIMEBase('application', "octet-stream")
            part.set_payload( open(f,"rb").read() )
            Encoders.encode_base64(part)
            part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
            msg.attach(part)
        smtp = smtplib.SMTP(self.server)
        mydict = smtp.sendmail(self.send_from, send_to, msg.as_string())
        if self.logger.isEnabledFor(logging.DEBUG):
            self.logger.debug("Email Successfully Sent!")
        smtp.close()
        return mydict

Nó được tạo một lần và chia sẻ giữa các lớp khác nhau cần khả năng gửi thư.

Vì vậy, đối với bạn, tạo một lớp larryvới các thuộc tính và phương thức bạn cần. Ở mọi nơi khách hàng nói larry = blahthay thế nó bằng larryObj.larry = blah. Điều này di chuyển mọi thứ đến các dự án phụ mà không phá vỡ giao diện hiện tại.

Điều khác duy nhất cần làm là tìm kiếm "đơn vị công việc". Nếu bạn định biến một phần của "Thần vật" thành phương pháp riêng của mình, hãy làm như vậy . Nhưng, đặt phương pháp bên ngoài nó. Điều này buộc bạn phải tạo một giao diện giữa các thành phần.

Đặt nền tảng đó cho phép mọi thứ khác đi theo nó. Ví dụ, một phần của đối tượng trợ giúp trình bày cách nó giao tiếp với người gửi thư:

#!/usr/bin/env python
'''This module holds a class to spawn various subprocesses'''
import logging, os, subprocess, time, dateAdditionLib, datetime, re

class spawner:
    def __init__(self, mailer):
        self.logger = logging.getLogger('dirMon.spawner')
        self.myMailer = mailer

Tập trung vào đơn vị công việc nhỏ nhất có thể, và di chuyển nó ra ngoài. Điều này dễ thực hiện hơn và cho phép bạn chơi với thiết lập nhanh chóng. Đừng nhìn vào các thuộc tính để di chuyển các công cụ, chúng phụ trợ cho các nhiệm vụ được thực hiện với chúng trong hầu hết các trường hợp. Bất cứ điều gì còn lại sau khi bạn đã xử lý các phương thức có lẽ nên trong đối tượng ban đầu, vì nó là một phần của trạng thái chia sẻ.

Nhưng , các đối tượng mới bây giờ sẽ chấp nhận các thuộc tính mà chúng cần làm biến init, không chạm vào thuộc tính đối tượng người gọi. Sau đó, họ trả về bất kỳ giá trị cần thiết nào, mà người gọi có thể sử dụng để cập nhật các thuộc tính được chia sẻ khi cần thiết. Điều này giúp tách rời các đối tượng và làm cho một hệ thống mạnh mẽ hơn.


1
Câu trả lời tuyệt vời, Spencer. Cảm ơn bạn! Tôi có một số câu hỏi tiếp theo quá cụ thể về bản chất để phù hợp ở đây. Tôi có thể liên lạc với bạn để thảo luận về những điều này?
Zearin

@Zearin chắc chắn, hồ sơ của tôi có địa chỉ email của tôi. Điều này là cho một dự án công ty, và tôi không thể cung cấp cho bạn một bản sao hoàn chỉnh của kho lưu trữ vì những thứ độc quyền trong đó. Trong một khoảng thời gian đủ, tôi có thể dọn dẹp trước / sau ảnh chụp nhanh, nhưng tôi không chắc điều đó sẽ giúp bạn bao nhiêu.
Spencer Rathbun

Tôi không thể thấy bất kỳ địa chỉ email nào trong hồ sơ của bạn. Có tất cả các loại thông tin, nhưng không liên lạc với thông tin. Tôi nên liên lạc với bạn như thế nào?
Zearin

Hiểu rồi. Cybermen: Xóa Xóa! Xóa bỏ! Xóa bỏ!"
Zearin
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.