第四天,继续加油,面向对象开始
使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,再创建对象,这样就可以通过对象去存储一些数据或调用相应的行为。先来看一个示例代码
class Person(object):
def __init__(self, name, age, father):
self.name = name
self.age = age
self.__father = father
def __speak_father(self):
print("my father is", self.__father)
def speak(self):
print("I'm a person, and my name is %s, and I'm %d years old" % (self.name, self.age))
def main():
person1 = Person("zhangsan", 12, "zhanger")
person2 = Person("lisi", 13, "lisan")
person1.speak()
person2.speak()
# person2.__speak_father()
# 访问私有方法
person2._Person__speak_father()
if __name__ == "__main__":
main()
这段代码中,__init__方法,是相当于Java中的构造方法,就是在创建对象时进行初始化操作,__speak_father这个方法是一个私有方法,规定上来说是不希望我们直接通过对象去调用这个方法,不过其实我们也通过main方法中的方式去调用,但这样就破坏规则了,不建议这么操作,_一个下划线开头的方法是受保护的方法,只能本类对象或子类对象去调用,speak方法就是一个普通方法,只要是Person的对象就可以去调用这个方法。
面向对象(Object Oriented,OO)是软件开发方法,面向对象指在程序设计中采用封装、继承、多态等设计方法,这三个也是面向对象开发最重要的三大特点,我们一个一个来看,什么是封装,隐藏对象的属性和实现细节,仅对外提供公共访问方式,这样我们在使用这些对象的时候,发生关系的只是我们开放的接口,并不需要知道具体的实现细节,而且对代码进行封装,可以一定程度上减少耦合,提高代码的重用性。继承就是类与类之间的关系了,使用已经存在的类的定义作为基础,在此基础上进行新类的定义,新的类可以扩展新的属性或方法,也可以使用父类定义好的功能。多态归结为一句话就是父类引用指向子类对象,这句话是Java中常说的,什么意思呢,就是一个引用变量的到底指向哪个类的实例对象,该引用变量发出的方法调用的是哪个类中实现的方法,必须在程序运行期间才能决定。关于面向对象,这里我就不详细描述了,具体可以去看看《Thinking in java》里面的介绍,额,因为我学Java的时候看的这部分印象很深刻,所以推荐大家也看一看,看这本书印象最深的一句话就是“万物皆对象”,还有点哲学的意思在里面,具体什么意思就自己去探索吧。
上面的示例代码给出了Person中的属性,两个公开的,一个私有的,这里我们这两种方式都是不推荐的,毕竟直接把对象的属性暴露给外界是有问题的,所以我们就把这些属性设置为受保护的,上文有提到过,就是加一个下划线_
。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:@property包装器包装getter和setter方法
class Person(object):
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
@name.setter
def name(self, name):
self._name = name
def speak(self):
print("I'm a person, and my name is %s, and I'm %d years old" % (self._name, self._age))
def main():
person = Person('zhangsan', 12)
person.speak()
person.age = 11
person.name = 'lisi'
person.speak()
print(person.age)
if __name__ == "__main__":
main()
这小段标题是什么意思呢,它是一个访问器,用以提供getter方法,代码中的@name.setter
用来提供setter方法,因为我们还是需要修改属性对应的值的,所以通过这两个访问器去做到修改和访问。直接通过对象名.属性名
的方式就能做到。
刚刚没介绍到,python是一门动态语言,就是我们可以在程序运行时给对象绑定新的属性或方法,也可以对已经绑定过的属性和方法进行解绑。而__slots__
就是对对象能绑定的属性进行限制,限制本类对象只能绑定元组中的属性。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:slots的使用
class Person(object):
__slots__ = ('_name', '_age', '_gender')
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@name.setter
def name(self, name):
self._name = name
@age.setter
def age(self, age):
self._age = age
def speak(self):
print("I'm a person, and my name is %s, and I'm %d years old" % (self.name, self.age))
def main():
person = Person('zhangsan', 12)
person._gender = '男'
print(person.name)
print(person._gender)
person._nana = "nana"
print(person._nana)
if __name__ == "__main__":
main()
这段代码你可以运行一下,然后看看控制台信息就豁然开朗了。
静态方法就是可以直接通过类名调用的方法,类方法和静态方法类似,第一个参数约定名为cls,它代表的是当前类相关的信息的对象,通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:静态方法和类方法
class AddUtil(object):
@staticmethod
def add(*num):
total = 0
for item in num:
total += item
print(total)
@classmethod
def add_num(cls):
return cls.add(1, 1, 1)
def main():
AddUtil.add(1, 1, 1, 1, 1, 1)
AddUtil.add_num()
if __name__ == "__main__":
main()
子类继承父类有可以对父类已有的方法进行重写,通过重写,我们可以让不同的子类调用相同的接口表现出不同的行为,就是上面提到的多态。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:方法的重写
from abc import abstractmethod, ABCMeta
class Person(object, metaclass=ABCMeta):
"""人类"""
@abstractmethod
def speak(self, name):
pass
class Student(Person):
"""学生类"""
def speak(self, name):
print("%s是本学生的名字" % name)
class Worker(Person):
"""工人类"""
def speak(self, name):
print("%s是本工人的名字" % name)
def main():
student = Student()
student.speak("zhangsan")
worker = Worker()
worker.speak("lisi")
if __name__ == "__main__":
main()
这里面Person类中定义了一个抽象方法,从而Person类就是一个抽象类,抽象类就是不可以创建对象的类,就是让别的类去继承这个类然后实现相应的功能,可以通过abc
模块的ABCMeta
元类和abstractmethod
包装器来达到抽象类的效果,上面的代码中Person类中的speak方法就是一个抽象方法,从而Person类不可以创建对象。
实际开发中对数据进行持久化操作,最直接的方式就是保存到文件中。python中文件的独写操作很简单,通过内置的open函数,就可以指定文件名、操作模式、编码信息等获得到可以操作文件的文件对象,接下来就可以对文件进行独写操作。操作模式见下表(骆昊的100天python教程中文件和异常中的表)。
操作模式 | 具体含义 |
---|---|
'r' |
读取 (默认) |
'w' |
写入(会先截断之前的内容) |
'x' |
写入,如果文件已经存在会产生异常 |
'a' |
追加,将内容写入到已有文件的末尾 |
'b' |
二进制模式 |
't' |
文本模式(默认) |
'+' |
更新(既可以读又可以写) |
读写文本文件时,三个上述,一是使用open函数时指定好带路径的文件名,二是操作模式,三是编码方式,路径可以是相对路径也可以是绝对路径,操作模式默认为r
,文件的编码方式与encoding参数指定的编码方式要一致,否则会导致读取失败。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:文件读取小例子
def main():
file = open('README.txt', 'r', encoding='utf-8')
print(file.read())
file.close()
if __name__ == "__main__":
main()
像上面的代码很有可能因为文件不存在或编码不一致无法打开,那么将引发异常状况导致程序崩溃。所以python提供异常机制来提高我们代码的容错性和健壮性。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:文件读取
def main():
file = None
try:
file = open('README.txt', 'r', encoding='utf-8')
print(file.read())
except FileNotFoundError:
print("无法打开该文件")
except LookupError:
print('指定了未知的编码!')
except UnicodeDecodeError:
print('读取文件时解码错误!')
finally:
file.close()
if __name__ == "__main__":
main()
我们可以将那些运行时可能报错的代码放在try代码块中,在try后面可跟随多个except来捕获可能发生的异常,最后使用finally来关闭打开的文件,释放资源,finally代码块不论程序正常还是异常都会执行,所以又称为"总是被执行代码块",用来作为释放资源的操作最合适了。还可以使用上下文语法,通过with关键字指定文件对象的上下文环境并离开上下文环境时自动释放文件资源。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:with
def main():
try:
with open('README.txt', 'r', encoding='utf-8') as file:
print(file.read())
except FileNotFoundError:
print("无法打开该文件")
except LookupError:
print('指定了未知的编码!')
except UnicodeDecodeError:
print('读取文件时解码错误!')
if __name__ == "__main__":
main()
文件对象还有readlines方法,可以将文件按行读取到一个列表容器中,代码如下。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:with
def main():
try:
with open('README.txt', 'r', encoding='utf-8') as file:
lines = file.readlines()
print(lines[0])
except FileNotFoundError:
print("无法打开该文件")
except LookupError:
print('指定了未知的编码!')
except UnicodeDecodeError:
print('读取文件时解码错误!')
if __name__ == "__main__":
main()
可以通过for-in去遍历,我只用0索引是因为我的文件只有一行。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:
def main():
try:
with open('123.png', 'rb') as file1:
data = file1.read()
print(data)
with open('copy_123.png', 'wb') as file2:
file2.write(data)
except FileNotFoundError as e:
print('指定的文件无法打开.')
except IOError as e:
print('读写文件时出现错误.')
print('程序执行结束.')
if __name__ == "__main__":
main()
json现在作为异构系统间交换数据的一种格式非常流行,关于json的知识,可以参考json的官方网站。
{
"name": "zhangshan",
"age": 12,
"books": [
{"name": "book1", "price": 12},
{"name": "book2", "price": 14}
]
}
和字典一样吧,和python对应关系如下。
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (int / real) | int / float |
true / false | True / False |
null | None |
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float, int- & float-derived Enums | number |
True / False | true / false |
None | null |
python中json模块使用如下。
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:json的使用
import json
def main():
someone = {
"name": "zhangshan",
"age": 12,
"books": [
{"name": "book1", "price": 12},
{"name": "book2", "price": 14}
]
}
try:
with open("data.json", 'w', encoding='utf-8') as file1:
json.dump(someone, file1)
except IOError as e:
print(e)
print("数据保存完成!")
if __name__ == "__main__":
main()
json模块中四个主要的函数:
dump
- 将Python对象按照JSON格式序列化到文件中dumps
- 将Python对象处理成JSON格式的字符串load
- 将文件中的JSON数据反序列化成对象loads
- 将字符串的内容反序列化成Python对象#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:json
import json
def main():
temp_dict = {'name': 'zhangsan', 'age': 12, 'role': 'student'}
json_str = json.dumps(temp_dict)
print(json_str)
# 因为文件存在所以就没有进行try-except
file = open('data.json', 'r')
temp_dict1 = json.load(file)
print(temp_dict1['name'])
file.close()
if __name__ == "__main__":
main()
第四天也看了不少,继续努力!
如果你发现我的文章哪里有错误或者有什么好的想法可以联系我,我们一起学习共同进步,我的邮箱地址是[email protected]
let’s do more of those!