商业数据分析从入门到入职(8)Python模块、文件IO和面向对象

CuterCorley
• 阅读 1702

前言

本文先介绍了Python中程序、模块和包的基本使用,并在此基础上介绍了Python标准库。然后详细介绍了Python中的文件IO操作,包括文本文件、二进制文件的读写和其他IO操作。最后介绍了面向对象,包括类的定义、继承的使用、鸭子类型和魔法方法。 商业数据分析从入门到入职(8)Python模块、文件IO和面向对象

一、程序、模块和包

1.自定义模块和包

之前我们使用的.ipynb文件都不是纯Python文件,纯Python文件应该是.py文件。

一个纯Python文件,如report.py可能如下:

def get_description():
    """Return random weather, just like the pros"""
    from random import choice
    possibilities = ['rain', 'snow', 'sleet', 'fog', 'sun', 'who knows']
    return choice(possibilities)

def get_desc():
    """Return random weather, just like the pros"""
    from random import choice
    possibilities = ['rain', 'snow', 'sleet', 'fog', 'sun', 'who knows']
    return choice(possibilities)

这个代码中包含了两个函数。

如需本节同步ipynb文件和Python文件,可以直接点击加QQ群 商业数据分析从入门到入职(8)Python模块、文件IO和面向对象963624318 在群文件夹商业数据分析从入门到入职中下载即可。

.ipynb文件也可以另存为.py文件,依次选择文件 → 下载 → Python(.py)即可,就会保存为与.ipynb文件同名的.py文件。 可以在Python IDE如PyCharm中运行Python文件,也可以在命令行中运行,假如你的Python文件在E:\Test目录下,名为test.py,则可以先在命令行中通过命令cd E:\Test切换到Python文件所在目录,然后再执行python test.py即可运行当前文件。

可以在其他程序中直接使用自己定义的Python代码。 例如可以在当前的.ipynb中使用之前的report.py中的get_description()函数(需要保证.ipynb文件与report.py位于同级目录),如下:

import report

report.get_description()

输出:

'rain'

可以看到,正常执行。

之前我们可能已经遇到过类似如下的情况:

import math
math.sqrt(2)

通过import关键字导入math,然后就可以使用它进行各种操作了。 但是并不是可以任意导入的,如下:

import corley

此时报错:

---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-2-fb30571dbbc8> in <module>
----> 1 import corley

ModuleNotFoundError: No module named 'corley'

即提示没有名为“corley”的模块。

但是math不在当前目录,也能正常使用,这是因为当前Python的模块搜索路径还包括其他路径,可以通过以下代码查看当前的模块搜索路径:

import sys
sys.path

输出:

['XXX',
 'E:\\Anaconda3\\python38.zip',
 'E:\\Anaconda3\\DLLs',
 'E:\\Anaconda3\\lib',
 'E:\\Anaconda3',
 '',
 'E:\\Anaconda3\\lib\\site-packages',
 'E:\\Anaconda3\\lib\\site-packages\\win32',
 'E:\\Anaconda3\\lib\\site-packages\\win32\\lib',
 'E:\\Anaconda3\\lib\\site-packages\\Pythonwin',
 'E:\\Anaconda3\\lib\\site-packages\\IPython\\extensions',
 'C:\\Users\\LENOVO\\.ipython']

除了第一个目录是档期那目录,其他目录都是Anaconda的相关路径,math模块可能就在某个路径下。

某些模块可能有长名称,在这种情况下,可以在导入时重命名模块,这样可以节省一些时间。 如下:

import report as rp
rp.get_description()

输出:

'sun'

可以看到,能达到同样的效果。

有些模块中可能有大量的定义,如果不需要全部导入,则可以从中导入需要使用的内容。 如下:

from report import get_description
get_description()

输出:

'fog'

也可以进行命名,如下:

from report import get_description as get_weather
get_weather()

输出:

'who knows'

从一行代码到多行函数,再到独立程序,再到同一目录中的多个模块,为了使Python应用程序更具规模,可以将模块组织成称为的文件层次结构。

例如,当前目录下有一个子目录为source,下有两个文件,daily.py如下:

def forecast():
    'fake daily forecast'
    return 'like yesterday'

weekly.py如下:

def forecast():
    """Fake weekly forecast"""
    return ['snow', 'more snow', 'sleet', 'freezing rain', 'rain', 'fog', 'hail']

此时可以从包source中的模块daily和weekly中导入forecast。 如下:

from source import daily, weekly

print('Daily forecast:', daily.forecast())
print('Weekly forecast:', weekly.forecast())

输出:

Daily forecast: like yesterday
Weekly forecast: ['snow', 'more snow', 'sleet', 'freezing rain', 'rain', 'fog', 'hail']

从Python3.3开始引入了隐式名称空间包,它允许我们创建一个不带init.py文件的包。

2.Python标准库

Python的一个突出的声明是它有“batteris included”——一个大型的标准模块库,它执行许多有用的任务,并被分开保存以避免核心语言膨胀。 不时地对Python的标准库进行一些探索总是很有帮助的。

Python的标准库非常广泛,提供了广泛的工具,如https://docs.python.org/3/library/列出的长目录所示。该库包含内置模块(用C编写),这些模块提供对系统功能(如Python程序员无法访问的文件I/O)的访问,以及用Python编写的模块,它们为日常编程中出现的许多问题提供标准化解决方案。其中一些模块被明确设计为鼓励和增强Python程序的可移植性,方法是将平台细节抽象到平台无关的api中。 用于Windows平台的Python安装程序通常包括整个标准库,并且通常还包括许多附加组件。对于类Unix的操作系统,Python通常是作为包的集合提供的,因此可能需要使用操作系统提供的打包工具来获取部分或全部可选组件。

例如datetime库的简单使用如下:

import datetime

dt = datetime.date(2020, 9, 28)
dt.weekday()

输出:

0

如果导入一个库报错ModuleNotFoundError,那说明这个库不是Python标准库,而是第三方库,需要下载安装之后才能导入。

安装第三方库可以使用condapip命令: 如安装jieba库(中文分词库)则可以执行conda install jiebapip install jieba来安装这个库,也可以到pip官网https://pypi.org/project/pip/查找所需要的库并下载安装。

二、文件IO

1.文件输入输出基本介绍

当程序运行时,所有生成的数据都存储在RAM中,RAM速度快,但有两个限制:

  • 昂贵(因此容量小)
  • 需要恒定电源

磁盘驱动器比RAM慢,但更便宜,并且更重要的是,即使断电也能保存数据,为了保持数据的持久性,我们需要将其作为文件存储在磁盘驱动程序中。 简单的持久性是一种最简单的平面文件,它只是存储在文件名下的字节序列,可以将文件读入内存并从内存写入文件(在磁盘驱动程序上)。

在读或写文件之前,必须先打开它:

fileobj = open(filename, mode)

mode是一个字符串,指示文件的类型以及要对其执行的操作: mode的第一个字母表示操作: 字符|含义 ----|----- r|读 w|写入(如果文件不存在,创建一个;如果文件存在,则重写它) x|写入(仅当文件不存在时) a|如果文件存在,则追加(在结尾后写入)

mode的第二个字母表示文件的类型: 字符|含义 -----|----- t(或无)|纯文本 b| 二进制

查看open()函数如下:

?open

输出:

Signature:
open(
    file,
    mode='r',
    buffering=-1,
    encoding=None,
    errors=None,
    newline=None,
    closefd=True,
    opener=None,
)
Docstring:
Open file and return a stream.  Raise OSError upon failure.

file is either a text or byte string giving the name (and the path
if the file isn't in the current working directory) of the file to
be opened or an integer file descriptor of the file to be
wrapped. (If a file descriptor is given, it is closed when the
returned I/O object is closed, unless closefd is set to False.)

mode is an optional string that specifies the mode in which the file
is opened. It defaults to 'r' which means open for reading in text
mode.  Other common values are 'w' for writing (truncating the file if
it already exists), 'x' for creating and writing to a new file, and
'a' for appending (which on some Unix systems, means that all writes
append to the end of the file regardless of the current seek position).
In text mode, if encoding is not specified the encoding used is platform
dependent: locale.getpreferredencoding(False) is called to get the
current locale encoding. (For reading and writing raw bytes use binary
mode and leave encoding unspecified.)

2.读写文本文件

简单使用如下:

text = '''\
First line
Second line
Third line
End
'''

print(len(text))

fout = open('new_file.txt', 'wt')
ret = fout.write(text)
print('return value of write() =', ret)
fout.close()

输出:

38
return value of write() = 38

write()方法的返回值为文件的长度。 同时可以看到,在同级目录下多了一个文件即为new_file.txt,内容如下:

First line
Second line
Third line
End

再读取文件如下:

fin = open('new_file.txt', 'r')
lines = fin.readlines()
print(lines)
fin.close()

输出:

['First line\n', 'Second line\n', 'Third line\n', 'End\n']

可以看到,将所有行读出并存到列表中。

再追加内容,如下:

text = '''\
1
2
3
'''

print(len(text))

fout = open('new_file.txt', 'a')
ret = fout.write(text)
print('return value of write() =', ret)
fout.close()

输出:

6
return value of write() = 6

再次查看new_file.txt,如下:

First line
Second line
Third line
End
1
2
3

可以看到,内容追加到之前的内容后面。

还可以通过print()函数实现将文本输出到文件中,如下:

text = '''\
hello
world
'''
fout = open('file.txt', 'w')
print(text, file=fout)
fout.close()

此时可以看到目录中多了一个文件为file.txt,内容为:

hello
world

这就是通过print()函数将内容输出到文件中。

Python有一个彩蛋,输入:

import this

输出:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

这就是Python之禅,定义了Python的一些规范。

可以分段写入文件,避免文件太大时导致的内存不足问题。 如下:

zen_of_py = '''\
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
'''

fout = open('zen_of_py.txt', 'w')
size = len(zen_of_py)
offset = 0
chunk = 100

while offset < size:
    fout.write(zen_of_py[offset : offset+chunk])
    offset += chunk

fout.close()

此时查看目录,多了文件为zen_of_py.txt; 这是通过分段的方式写入文件的,类似于从网上下载文件分段下载。

读文件也能分段读取,也可以避免文件太大时内存不足。 如下:

zen_of_py = ''
fin = open('zen_of_py.txt', 'r')
chunk = 100

while True:
    fragment = fin.read(chunk)
    if not fragment:
        break
    zen_of_py += fragment

fin.close()
print(zen_of_py)

输出:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

也可以逐行读取文件。 如下:

fin = open('zen_of_py.txt', 'r')

while True:
    line = fin.readline()
    if not line:
        break
    print(line, end='')

fin.close()

输出与前面相同。

3.读写二进制文件

生成二进制数据如下:

bdata = bytes(range(256))
print(len(bdata))
print(bdata[0])
print(bdata[255])

写入二进制字节如下:

fout = open('bfile', 'wb')
ret = fout.write(bdata)
fout.close()
print('Write %d bytes' % ret)

输出:

Write 256 bytes

图片、音频、视频等文件都属于二进制文件。

也可以分段写入:

fout = open('bfile', 'wb')
size = len(bdata)
offset = 0
chunk = 100

while offset < size:
    ret = fout.write(bdata[offset : offset+chunk])
    print(ret)
    offset += chunk

fout.close()

输出:

100
100
56

读取二进制文件如下:

fin = open('bfile', 'rb')
bdata = fin.read()
fin.close()
len(bdata)

输出:

256

4.使用with自动关闭文件

如果打开了一个文件却忘了关闭它,当跳出打开文件的范围时,Python将关闭它。但是,更安全的方法是使用with关键字。

如下:

with open('tmp1', 'w') as fout:
    fout.write(text)

此时在当前目录下会生成文件tmp1。

5.获取和改变位置

读写时,Python会跟踪在文件中的当前位置,tell()返回文件开头的当前偏移量(字节),seek()跳转到文件中的另一个字节偏移量。

对文本文件的用法如下:

fin = open('tmp1', 'r')
print('starting pos =', fin.tell())
line = fin.readline()
print('# char read =', len(line))
print('ending pos =', fin.tell())
fin.close()

输出:

starting pos = 0
# char read = 6
ending pos = 7

这只适用于ASCII编码的文本文件,其中每个字符都存储在一个字节中。像UTF-8这样的编码可以使用每个字符不同数量的字节。

对二进制文件的操作如下:

fin = open('bfile', 'rb')
fin.seek(255)
bdata = fin.read()

print(len(bdata))
print(bdata[0])

输出:

1
255

seek()方法也可以使用第二个参数,即seek(offset, origin): 如果origin为0(默认值),则从开始处偏移字节; 如果origin为1,则从当前位置偏移; 如果origin为2,则相对于终点偏移字节(即偏移量必须为负)。 origin不是关键字参数。

如下:

fin = open('bfile', 'rb')
fin.seek(-1, 2)
bdata = fin.read()

print(len(bdata))
print(bdata[0])

输出:

1
255

6.结构化文本文件

对于纯文本文件,唯一的组织级别是行(用换行符分隔),有时可能想要一个更丰富的结构,一种方法是引入额外的分隔符。

常见的形式如下:

  • Comma-Separated Values (CSV): \t,|
  • HTML & XML: <>
  • JSON: {}[]

写入csv如下:

import csv

dc_heros = [
    ['Flash', 'Barry Allen'],
    ['Green Arrow', 'Oliver Queen'],
    ['Atom', 'Ray Palmer'],
    ['Bat Man', 'Bruce Wayne']
]

with open('dc_heros.csv', 'w') as fout:
    csvout = csv.writer(fout)
    csvout.writerows(dc_heros)

执行后,可以看到目录中生成了dc_heros.csv文件。

读csv文件如下:

with open('dc_heros.csv', 'r') as fin:
    csvin = csv.reader(fin)
    dc_heros = [row for row in csvin]

print(dc_heros)

输出:

[['Flash', 'Barry Allen'], [], ['Green Arrow', 'Oliver Queen'], [], ['Atom', 'Ray Palmer'], [], ['Bat Man', 'Bruce Wayne'], []]

三、面向对象

Object Oriented Programming即面向对象程序设计。 Python中一切皆是对象,从数字到模块。 然而,Python通过特殊语法的mena隐藏了大部分对象机制,例如可以直接输入num=7来创建一个值为7的integer类型的对象,并为名称num指定一个对象引用。 唯一需要查看对象内部的时间是想要创建自己的对象或修改现有对象的行为时。

对象包含:

  • 数据(变量,称为属性
  • 代码(函数,称为方法

它代表了一个特殊的例子: 把物体看作名词,把它们的方法看作动词。

1.用class定义类

如果一个对象像一个盒子,那么一个类就像制造盒子的模具。

定义一个空对象如下:

class Person():
    pass

someone = Person()
type(someone)

输出:

__main__.Person

可以看到,定义了一个叫Person的类,并通过这个类实例化了一个实例,即someone对象。

生成一个整型对象,如下:

a = int(2)
type(a)

输出:

int

可以在生成对象即初始化时就给对象一些特征,此时可以给类定义__init__()方法即初始化方法。

如下:

class Person():
    def __init__(self, name, gender): # The first parameter has to be self
        self.name = name
        self.gender = gender

ed = Person('Edward', 'Male')

可以看到,__init__()方法中传递了self参数,这是在类中定义实例方法必须要带的参数,代表的是个体对象本身。 创建对象的大概过程如下: (1)查找Person类的定义; (2)在内存中创建新对象; (3)调用__init__()方法,将新创建的对象作为self传递,其他对象作为name和gender传递; (4)在对象中存储name和gender的值; (5)返回新对象; (6)将创建的对象赋值给ed。

此时可以获取到创建出来的对象的属性,如下:

print('Name:', ed.name)
print('Gender:', ed.gender)

输出:

Name: Edward
Gender: Male

还可以修改对象的属性,如下:

ed.name = 'Corley'
ed.name

输出:

'Corley'

还可以在类中定义方法。 如下:

class Person():
    def __init__(self, name, gender): # The first parameter has to be self
        self.name = name
        self.gender = gender

    def say(self):
        print("Hi I'm " + self.name + ", it's nice to meet you!")

ed = Person('Corley', 'Male')
ed.say()

输出:

Hi I'm Corley, it's nice to meet you!

2.继承

还可以从现有类中创建一个新类,但需要添加或更改,这就是继承。

定义一个继承自Person类的类:

class MDPerson(Person):
    pass

ed = MDPerson("Corley", 'Male')
ed.say()

输出:

Hi I'm Corley, it's nice to meet you!

可以看到,MDPerson类继承自Person类,虽然其内部没有实现其他代码,但是继承了父类中的全部特性,因此可以进行初始化和调用函数; 其中,MDPerson类称为子类,Person类称为父类

子类还可以扩展新特性。 如下:

class MDPerson(Person):
    def diagnose(self):
        print('You need some treatment.')

ed = MDPerson("Corley", 'Male')
ed.diagnose()

输出:

You need some treatment.

但是父类中不能调用子类中新实现的特性。 如下:

someone = Person('Someone', 'NA')
someone.diagnose()

会报错:

----------------------------------------------------------------
AttributeError                 Traceback (most recent call last)
<ipython-input-11-e646e6a12c4e> in <module>
      1 someone = Person('Someone', 'NA')
----> 2 someone.diagnose()

AttributeError: 'Person' object has no attribute 'diagnose'

子类中可以重新定义父类中已经定义过的方法,称之为重写。 如下:

class MDPerson(Person):
    def __init__(self, name, gender, dept='Cardiac Surgery'):
        self.name = 'Doctor ' + name
        self.gender = gender
        self.dept = dept

    def say(self):
        print("Hi I'm %s from %s department, how can I help you" % (self.name, self.dept))

ed = MDPerson("Corley", 'Male')
ed.say()

输出:

Hi I'm Doctor Corley from Cardiac Surgery department, how can I help you

显然,此时与父类中的方法不同。

可以通过super()继承来自父类的特性,从而同时实现付类和子类的特性。

如下:

class MDPerson(Person):
    def __init__(self, name, gender, dept='Cardiac Surgery'):
        super().__init__(name, gender)
        self.name = 'Doctor ' + self.name
        self.dept = dept

    def say(self):
        super().say()
        print("Hi I'm %s from %s department, how can I help you" % (self.name, self.dept))  

ed = MDPerson("Corley", 'Male')
ed.say()

输出:

Hi I'm Doctor Corley, it's nice to meet you!
Hi I'm Doctor Corley from Cardiac Surgery department, how can I help you

如果Person的定义将来发生更改,那么使用super()将确保MDPerson从Person继承的属性和方法将反映更改。

3.子父类调用

Python使用self参数来查找正确对象的属性和方法。

如下:

ed = Person('Corley', 'Male')
ed.say()
Person.say(ed)

输出:

Hi I'm Corley, it's nice to meet you!
Hi I'm Corley, it's nice to meet you!

可以看到,两种调用方式的效果相同,这是因为类中实现的实例方法本来就有一个参数self,代表对象自己,如果传一个参数,只要这个参数是对象,也是能够正常执行的。

背后执行的逻辑如下: (1)查找对象ed的类(Person); (2)将对象ed作为self参数传递给Person类的say()方法。

再如:

ed = MDPerson('Corley', 'Male')
print(ed.name)
Person.say(ed)

输出:

Doctor Corley
Hi I'm Doctor Corley, it's nice to meet you!

可以看到,先使用MDPerson类进行初始化,初始化后ed对象的name属性为Doctor Corley,其父类Person再调用say()方法,并将ed作为对象传递进去,因此打印出的不是Hi I'm Corley, it's nice to meet you!,也不是Hi I'm Doctor Corley from Cardiac Surgery department, how can I help you,而是Hi I'm Doctor Corley, it's nice to meet you!

4.鸭子类型

Python对多态性有一个松散的实现,这意味着它对不同的对象应用相同的操作,而不管它们是什么类。 同样,这也是EAFP设计模式的一部分。Python还会假设一个对象有这样的方法,并尝试调用它。如果没有找到方法,它将抛出异常。

如下:

class Quote():
    def __init__(self, person, words):
        self.person = person
        self.words = words
    def who(self):
        return self.person
    def says(self):
        return self.words + '.'

class QuestionQuote(Quote):
    def says(self):
        return self.words + '?'

class ExclamationQuote(Quote):
    def says(self):
        return self.words + '!'


def who_says(obj):
    print(obj.who(), 'says:', obj.says())


q1 = Quote('Edward', 'Normal quote')
q2 = QuestionQuote('Corley', 'Question quote')
q3 = ExclamationQuote('Jack', 'Exclamation quote')

quotes = [q1, q2, q3]

for q in quotes:
    who_says(q)

输出:

Edward says: Normal quote.
Corley says: Question quote?
Jack says: Exclamation quote!

再定义一个与前面的3个类无关系的类,如下:

class Ed():
    def who(self):
        return 'Ed'
    def says(self):
        return "Hi I'm Edward :)"

ed = Ed()
who_says(ed)

输出:

Ed says: Hi I'm Edward :)

与前面的类混合使用:

quotes = [q1, q2, q3, ed]

for q in quotes:
    who_says(q)

Ed.says(q3)

输出:

Edward says: Normal quote.
Corley says: Question quote?
Jack says: Exclamation quote!
Ed says: Hi I'm Edward :)

"Hi I'm Edward :)"

可以看到,即便Ed类和ExclamationQuote类毫无关系,但是ExclamationQuote对象还是可以作为参数传递到Education类的say()方法中。

如果它像鸭子一样走路,像鸭子一样嘎嘎叫,那它就是鸭子。这也就是多态,使用起来比其它语言更加灵活,没有类继承等方面的严格限制。

5.特殊方法

当输入像a = 3 + 8这样的代码时,可能想知道整数对象是如何知道如何实现+运算的,并且是如何使用=得到结果的,这些操作符使用了Python的特殊方法(又名魔法方法)。

这些特殊方法的名称都以双下划线__开始和结束,就像__init__()方法一样。

例如,在为重写__str__()方法时,打印对象如下:

print(q1)

输出:

<__main__.Quote object at 0x0000020B6FA17400>

显然,格式很不友好。

此时可以重写__str__()方法,来自定义打印对象的形式,如下;

class Quote():
    def __init__(self, person, words):
        self.person = person
        self.words = words
    def __str__(self):
        return 'Quote(%s, %s)' %(self.person, self.words)
    def who(self):
        return self.person
    def says(self):
        return self.words + '.'

q1 = Quote('Edward', 'Normal quote')
print(q1)

输出:

Quote(Edward, Normal quote)

显然,此时打印对象时,是打印的自定义字符串。

而如果不通过打印、而是直接像如下方式输出:

q1

会输出:

<__main__.Quote at 0x20b6f5c2970>

这可以通过__repr__()重写方法,如下:

class Quote():
    def __init__(self, person, words):
        self.person = person
        self.words = words
    def __str__(self):
        return 'Quote(%s, %s)' %(self.person, self.words)
    def __repr__(self):
        return 'Quote(%s, %s)' %(self.person, self.words)

    def who(self):
        return self.person
    def says(self):
        return self.words + '.'

q1 = Quote('Edward', 'Normal quote')
q1

输出:

Quote(Edward, Normal quote)

还有很多其他的魔法方法,可以根据需要选择使用。

总结

Python之所以可以获得广泛的应用,一个很重要的原因就是广泛的库支持,不仅可以自定义模块和包,还可以使用Python标准库和第三方库,大大增加了Python的功能。Python中的文件IO操作也很方便,可以对文本文件、二进制文件、格式化文件进行读写,并进行其他文件操作。面向对象是Python的一大特性,但是相比于其他语言限制更少、更加灵活,具有继承、多态等特性,可以灵活使用。

本文原文首发来自博客专栏数据分析,由本人转发至https://www.helloworld.net/p/4eYyFnZUQAC8G,其他平台均属侵权,可点击https://blog.csdn.net/CUFEECR/article/details/108862933查看原文,也可点击https://blog.csdn.net/CUFEECR浏览更多优质原创内容。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Karen110 Karen110
3年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这