【问题标题】:What is the proper way to delineate modules and classes in Python?在 Python 中描述模块和类的正确方法是什么?
【发布时间】:2013-05-28 01:44:20
【问题描述】:

我是 Python 新手,我开始学习代码结构的基础知识。我有一个正在开发的基本应用程序up on my Github

对于我的简单应用程序,我创建了一个基本的“类似 Evernote”的服务,允许用户创建和编辑笔记列表。在早期的设计中,我有一个 Note 对象和一个 Notepad 对象,它实际上是一个笔记列表。目前,我有以下文件结构:

Notes.py
| 
|------ Notepad (class)
|------ Note (class)

根据我目前的理解和实现,这转化为具有 Notepad 类和 Note 类的“Notes”模块,所以当我进行导入时,我说的是“从 Notes 导入 Notepad / 从 Notes 导入 Note”。

这是正确的方法吗?我觉得,出于 Java 的习惯,我应该有一个 Notes 文件夹和两个类作为单独的文件。

我的目标是了解最佳实践是什么。

【问题讨论】:

  • 与 java 不同,您可以将任何您想要的内容添加到文件顶层:类、函数、变量...不用担心。
  • 这是个人喜好,python 允许任何你想要的结构。您甚至可以在您的notes.py 文件中包含您的Notepad 类和您的Note 类!!!!您可以查看开源 python 项目以了解一些大型项目的结构
  • 我会先尝试设计,然后再看看什么最有意义。例如,将所有“数据层”类放在一个包中(单独的文件与否无关),将“表示层”放在另一个包中,并尝试看看我是否可以在“基础”中抽象出任何横切关注点类/包

标签: python class module


【解决方案1】:

只要类比较小,就将它们放在一个文件中。 如有必要,您仍然可以稍后移动它们。 实际上,较大的项目具有相当深的层次结构但向用户展示更扁平的层次结构是很常见的。因此,如果您稍后移动内容但仍希望 notes.Note 即使类 Note 移动得更深,只需将 note.path.to.module.Note 导入 notes 并且用户可以从那里获取它会很简单。你不必这样做,但你可以。因此,即使您稍后改变主意但想保留 API,也没有问题。

【讨论】:

    【解决方案2】:

    我自己一直在使用类似的应用程序。我不能说这是最好的方法,但它对我很有帮助。这些类旨在在用户发出请求(http 请求,这是一个 web 应用程序)时与数据库(上下文)进行交互。

    # -*- coding: utf-8 -*-
    import json
    import datetime
    
    class Note ():
        """A note. This class is part of the data model and is instantiated every
        time there access to the database"""
        def __init__(self, noteid = 0, note = "", date = datetime.datetime.now(), context = None):
            self.id = noteid
            self.note = note
            self.date = date
            self.ctx = context #context holds the db connection and some globals
    
        def get(self):
            """Get the current object from the database. This function needs the
            instance to have an id"""
            if id == 0:
                raise self.ctx.ApplicationError(404, ("No note with id 0 exists"))
            cursor = self.ctx.db.conn.cursor()
            cursor.execute("select note, date from %s.notes where id=%s" % 
                           (self.ctx.db.DB_NAME, str(self.id)))
            data = cursor.fetchone()
            if not data:
                raise self.ctx.ApplicationError(404, ("No note with id " 
                                                     + self.id + " was found"))
            self.note = data[0]
            self.date = data[1]
            return self
    
        def insert(self, user):
            """This function inserts the object to the database. It can be an empty
            note. User must be authenticated to add notes (authentication handled
            elsewhere)"""
            cursor = self.ctx.db.conn.cursor()
            query = ("insert into %s.notes (note, owner) values ('%s', '%s')" % 
                           (self.ctx.db.DB_NAME, str(self.note), str(user['id'])))
            cursor.execute(query)
            return self
    
        def put(self):
            """Modify the current note in the database"""
            cursor = self.ctx.db.conn.cursor()
            query = ("update %s.notes set note = '%s' where id = %s" % 
                     (self.ctx.db.DB_NAME, str(self.note), str(self.id)))
            cursor.execute(query)
            return self
    
        def delete(self):
            """Delete the current note, by id"""
            if self.id == 0:
                raise self.ctx.ApplicationError(404, "No note with id 0 exists")
            cursor = self.ctx.db.conn.cursor()
            query = ("delete from %s.notes where id = %s" % 
                     (self.ctx.db.DB_NAME, str(self.id)))
            cursor.execute(query)
    
        def toJson(self):
            """Returns a json string of the note object's data attributes"""
            return json.dumps(self.toDict())
    
        def toDict(self):
            """Returns a dict of the note object's data attributes"""
            return {
                    "id" : self.id,
                    "note" : self.note,
                    "date" : self.date.strftime("%Y-%m-%d %H:%M:%S")
                    }
    
    
    class NotesCollection():
        """This class handles the notes as a collection"""
        collection = []
        def get(self, user, context):
            """Populate the collection object and return it"""
            cursor = context.db.conn.cursor()
            cursor.execute("select id, note, date from %s.notes where owner=%s" % 
                           (context.db.DB_NAME, str(user["id"])))
            note = cursor.fetchone()
            while note:
                self.collection.append(Note(note[0], note[1],note[2]))
                note = cursor.fetchone()
            return self
    
        def toJson(self):
            """Return a json string of the current collection"""
            return json.dumps([note.toDict() for note in self.collection])
    

    我个人使用 python 作为“完成它”的语言,并且不为细节烦恼。这显示在上面的代码中。但是有一条建议:python 中没有私有变量或方法,所以不要费心去创建它们。让您的生活更轻松,快速编码,完成它

    使用示例:

    class NotesCollection(BaseHandler):
        @tornado.web.authenticated
        def get(self):
            """Retrieve all notes from the current user and return a json object"""
            allNotes = Note.NotesCollection().get(self.get_current_user(), settings["context"])
            json = allNotes.toJson()
            self.write(json)
    
        @protected
        @tornado.web.authenticated
        def post(self):
            """Handles all post requests to /notes"""
            requestType = self.get_argument("type", "POST")
            ctx = settings["context"]
            if requestType == "POST":
    
                Note.Note(note = self.get_argument("note", ""), 
                          context = ctx).insert(self.get_current_user())
            elif requestType == "DELETE":
                Note.Note(id = self.get_argument("id"), context = ctx).delete()
            elif requestType == "PUT":
                Note.Note(id = self.get_argument("id"),
                                  note = self.get_argument("note"),
                                  context = ctx).put()
            else:
                raise ApplicationError(405, "Method not allowed")
    

    通过使用装饰器,我可以从主代码中获得用户身份验证和错误处理。这使它更清晰,更容易维护。

    【讨论】:

    • 谢谢!这太棒了;您的示例在概念上与我的示例非常相似,明天当我开始实现 SQLite 持久性时,我可能会关注这个。非常感谢!
    • 由于您对我的回答表示赞赏,我花了一些时间改进此代码并添加了一些“文档字符串”。我希望你觉得这很有用:)
    猜你喜欢
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 2020-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-16
    相关资源
    最近更新 更多