我已经在python中编程了大约两年; 主要是数据(pandas,mpl,numpy),还有自动化脚本和小型Web应用程序.我正在努力成为一个更好的程序员并增加我的python知识,困扰我的一件事是我从未使用过类(除了为小型web应用程序复制随机烧瓶代码之外).我一般都明白它们是什么,但我似乎无法理解为什么我需要它们通过一个简单的功能.
为了增加我的问题的特异性:我写了大量的自动报告,这些报告总是涉及从多个数据源(mongo,sql,postgres,apis)中提取数据,执行大量或少量数据修改和格式化,将数据写入csv/excel/html,通过电子邮件发送出去.脚本范围从~250行到~600行.我是否有理由使用课程来完成这项工作?为什么?
dan*_*ton 97
类是面向对象编程的支柱.OOP高度关注代码组织,可重用性和封装.
首先,免责声明:OOP与功能编程部分形成对比,后者是Python中使用的不同范例.并非每个使用Python(或肯定是大多数语言)编程的人都使用OOP.你可以在不太面向对象的Java 8中做很多事情.如果您不想使用OOP,那么请不要.如果您只是编写一次性脚本来处理您再也不会使用的数据,那么请继续按照您的方式编写.
但是,使用OOP有很多原因.
一些原因:
组织:OOP定义了在代码中描述和定义数据和过程的众所周知的标准方法.数据和过程都可以存储在不同的定义级别(在不同的类中),并且有关于这些定义的标准方法.也就是说,如果您以标准方式使用OOP,它将帮助您以后的自己和其他人理解,编辑和使用您的代码.此外,您可以命名数据结构并方便地引用它们,而不是使用复杂的,任意的数据存储机制(dicts或列表或dicts或集合的dicts列表或其他).
状态:OOP可帮助您定义和跟踪状态.例如,在一个典型的例子中,如果您正在创建一个处理学生的程序(例如,成绩计划),您可以在一个地方(姓名,年龄,性别,年级,课程,成绩,教师,同龄人,饮食,特殊需要等),只要对象存活,并且易于访问,这些数据就会持久存在.
封装:通过封装,程序和数据一起存储.方法(函数的OOP术语)与它们操作和生成的数据一起定义.在Java等允许访问控制的语言中,或者在Python中,根据您描述公共API的方式,这意味着可以向用户隐藏方法和数据.这意味着如果您需要或想要更改代码,您可以执行任何您想要的代码实现,但保持公共API相同.
继承:继承允许您在一个位置(在一个类中)定义数据和过程,然后稍后覆盖或扩展该功能.例如,在Python中,我经常看到人们创建类的子dict类以添加其他功能.一个常见的更改是覆盖当从不存在的字典请求密钥以基于未知密钥提供默认值时抛出异常的方法.这允许您现在或以后扩展自己的代码,允许其他人扩展您的代码,并允许您扩展其他人的代码.
可重用性:所有这些原因和其他原因使代码具有更高的可重用性.面向对象的代码允许您编写一次实体(已测试)代码,然后一遍又一遍地重复使用.如果需要针对特定用例调整某些内容,则可以从现有类继承并覆盖现有行为.如果您需要更改某些内容,则可以在保留现有公共方法签名的同时更改所有内容,并且没有人更明智(希望如此).
同样,有几个原因不使用OOP,你不需要.但幸运的是,使用像Python这样的语言,你可以使用一点点或者很多,这取决于你.
学生用例的例子(不保证代码质量,只是一个例子):
面向对象
class Student(object):
def __init__(self, name, age, gender, level, grades=None):
self.name = name
self.age = age
self.gender = gender
self.level = level
self.grades = grades or {}
def setGrade(self, course, grade):
self.grades[course] = grade
def getGrade(self, course):
return self.grades[course]
def getGPA(self):
return sum(self.grades.values())/len(self.grades)
# Define some students
john = Student("John", 12, "male", 6, {"math":3.3})
jane = Student("Jane", 12, "female", 6, {"math":3.5})
# Now we can get to the grades easily
print(john.getGPA())
print(jane.getGPA())
Run Code Online (Sandbox Code Playgroud)
标准字典
def calculateGPA(gradeDict):
return sum(gradeDict.values())/len(gradeDict)
students = {}
# We can set the keys to variables so we might minimize typos
name, age, gender, level, grades = "name", "age", "gender", "level", "grades"
john, jane = "john", "jane"
math = "math"
students[john] = {}
students[john][age] = 12
students[john][gender] = "male"
students[john][level] = 6
students[john][grades] = {math:3.3}
students[jane] = {}
students[jane][age] = 12
students[jane][gender] = "female"
students[jane][level] = 6
students[jane][grades] = {math:3.5}
# At this point, we need to remember who the students are and where the grades are stored. Not a huge deal, but avoided by OOP.
print(calculateGPA(students[john][grades]))
print(calculateGPA(students[jane][grades]))
Run Code Online (Sandbox Code Playgroud)
Dmi*_*ich 19
每当你需要维持你的功能状态时,它就无法用发生器完成(产生而不是返回的功能).发电机保持自己的状态.
如果要覆盖任何标准运算符,则需要一个类.
每当您使用访客模式时,您都需要课程.使用生成器,上下文管理器(它们也更好地实现为生成器而不是类)和POD类型(字典,列表和元组等),可以更有效和干净地完成所有其他设计模式.
如果你想编写"pythonic"代码,你应该更喜欢上下文管理器和生成器而不是类.它会更清洁.
如果要扩展功能,您几乎总能通过包含而不是继承来完成它.
每个规则都有例外.如果要快速封装功能(即编写测试代码而不是库级可重用代码),可以将状态封装在类中.它很简单,不需要重复使用.
如果你需要一个C++风格的析构函数(RIIA),你肯定不想使用类.你想要上下文管理器.
关于为什么 OOP 有用,dantiston 给出了一个很好的答案。然而,值得注意的是,在大多数情况下,OOP 不一定是更好的选择。OOP的优点是将数据和方法结合在一起。在应用方面,我想说,只有当所有函数/方法都在处理并且只处理一组特定的数据而不处理其他任何事情时,才使用 OOP。
考虑对牙科示例进行函数式编程重构:
def dictMean( nums ):
return sum(nums.values())/len(nums)
# It's good to include automatic tests for production code, to ensure that updates don't break old codes
assert( dictMean({'math':3.3,'science':3.5})==3.4 )
john = {'name':'John', 'age':12, 'gender':'male', 'level':6, 'grades':{'math':3.3}}
# setGrade
john['grades']['science']=3.5
# getGrade
print(john['grades']['math'])
# getGPA
print(dictMean(john['grades']))
Run Code Online (Sandbox Code Playgroud)
乍一看,似乎所有 3 种方法都专门处理 GPA,直到您意识到Student.getGPA()可以将其泛化为计算 dict 平均值的函数,并在其他问题上重复使用,而其他 2 种方法则重新发明了 dict已经可以了。
功能实现收益:
class或selfs。功能实现丢失:
'name', 'age','gender'不是很干燥(不要重复)。通过将 dict 更改为列表可以避免这种情况。当然,列表不如字典清晰,但如果您无论如何在下面包含自动测试代码,这都不是问题。此示例未涵盖的问题:
__init__(self)。| 归档时间: |
|
| 查看次数: |
50349 次 |
| 最近记录: |