4、【Python】Python 3入门(模块/面向对象/错误和异常/文件操作/序列化/命名规范)

一、模块

    编写模块有很多种方法,其中最简单的一种便是创建一个包含函数与变量、以 .py 为后缀的文件。

    另一种方法是使用撰写 Python 解释器本身的本地语言来编写模块。举例来说,你可以使用 C 语言来撰写 Python 模块,并且在编译后,你可以通过标准 Python 解释器在你的 Python 代码中使用它们。

    模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 Python 标准库的方法。

    当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。

    搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块,需要把命令放在脚本的顶端。

    一个模块只会被导入一次,这样可以防止导入模块被一遍又一遍地执行。

    搜索路径被存储在 sys 模块中的 path 变量。当前目录指的是程序启动的目录。

1、导入模块
import module1[, module2[, ... moduleN]]

从模块中导入一个指定的部分到当前命名控件中:

from modname import name1[, name2[, ... nameN]]

把一个模块的所有内容全都导入当前的命名空间:

from modname import *
2、__name__属性

    每个模块都有一个 name 属性,当其值是 ‘main’ 时,表明该模块自身在运行,否则是被引入。

    一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用 name 属性来使该程序块仅在该模块自身运行时执行。

if __name__ == '__main__':
    print('程序自身在运行')
else:
    print('我来自另一模块')
3、dir函数

    内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回。

    如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称。

    在 Python 中万物皆对象,int、str、float、list、tuple等内置数据类型其实也是类,也可以用 dir(int) 查看 int 包含的所有方法。也可以使用 help(int) 查看 int 类的帮助信息。

4、包

    包是一种管理 Python 模块命名空间的形式,采用”点模块名称”。

    比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。

    就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。

    在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。

    目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。

    最简单的情况,放一个空的 init.py 文件就可以了。当然这个文件中也可以包含一些初始化代码或者为 all 变量赋值。

5、第三方模块
  • easy_install 和 pip 都是用来下载安装 Python 一个公共资源库 PyPI 的相关资源包的,pip 是
    easy_install 的改进版,提供更好的提示信息,删除 package 等功能。老版本的 python 中只有
    easy_install,没有pip。
  • easy_install 打包和发布 Python 包,pip 是包管理。

easy_install 的用法

  • 安装一个包
easy_install 包名
easy_install "包名 == 包的版本"
  • 升级一个包
easy_install -U "包名 >= 包的版本号"

pip 的用法

  • 安装一个包
pip install 包名pip install 包名 == 包的版本号
  • 升级一个包
pip install —upgrade 包名 >= 包的版本号
  • 删除一个包
pip uninstall 包名
  • 以安装包列表
pip list

二、面向对象

    类与对象是面向对象编程的两个主要方面。一个类(Class)能够创建一种新的类型(Type),其中对象(Object)就是类的实例(Instance)。可以这样来类比:你可以拥有类型 int 的变量,也就是说存储整数的变量是 int 类的实例(对象)。

  • 类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 方法:类中定义的函数。
  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 实例变量:定义在方法中的变量,只作用于当前实例的类。
  • 继承:即一个派生类(derived class)继承基类(base
    class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。
  • 实例化:创建一个类的实例,类的具体对象。
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

    Python 中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。

    对象可以包含任意数量和类型的数据。

1、self

self 表示的是当前实例,代表当前对象的地址。类由 self.class 表示。

self 不是关键字,其他名称也可以替代,但 self 是个通用的标准名称。

2、类

    类由 class 关键字来创建。 类实例化后,可以使用其属性,实际上,创建一个类之后,可以通过类名访问其属性。

3、对象方法

    方法由 def 关键字定义,与函数不同的是,方法必须包含参数 self, 且为第一个参数,self 代表的是本类的实例。

3、类方法

    装饰器 @classmethod 可以将方法标识为类方法。类方法的第一个参数必须为 cls,而不再是 self。

4、静态方法

    装饰器 @staticmethod 可以将方法标识为静态方法。静态方法的第一个参数不再指定,也就不需要 self 或 cls。

5、__init__方法

    init 方法即构造方法,会在类的对象被实例化时先运行,可以将初始化的操作放置到该方法中。如果重写了 init,实例化子类就不会调用父类已经定义的 init

6、变量

    类变量(Class Variable)是共享的(Shared)——它们可以被属于该类的所有实例访问。该类变量只拥有一个副本,当任何一个对象对类变量作出改变时,发生的变动将在其它所有实例中都会得到体现。

    对象变量(Object variable)由类的每一个独立的对象或实例所拥有。在这种情况下,每个对象都拥有属于它自己的字段的副本,也就是说,它们不会被共享,也不会以任何方式与其它不同实例中的相同名称的字段产生关联。

    在 Python 中,变量名类似 xxx 的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是 private 变量,所以,不能用 namescore 这样的变量名。

7、访问控制
  • 私有属性
        __private_attr:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。

  • 私有方法
        __private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用,不能在类地外部调用。

    我们还认为约定,一个下划线开头的属性或方法为受保护的。比如,_protected_attr、_protected_method。

8、继承

    类可以继承,并且支持继承多个父类。在定义类时,类名后的括号中指定要继承的父类,多个父类之间用逗号分隔。

    子类的实例可以完全访问所继承所有父类的非私有属性和方法。

    若是父类中有相同的方法名,而在子类使用时未指定,Python 从左至右搜索,即方法在子类中未找到时,从左到右查找父类中是否包含方法。

9、方法重写

    子类的方法可以重写父类的方法。重写的方法参数不强制要求保持一致,不过合理的设计都应该保持一致。

    super() 函数可以调用父类的一个方法,以多继承问题。

10、类的转悠方法:
  • init: 构造函数,在生成对象时调用
  • del: 析构函数,释放对象时使用
  • repr: 打印,转换
  • setitem: 按照索引赋值
  • getitem: 按照索引获取值
  • len: 获得长度
  • cmp: 比较运算
  • call: 函数调用
  • add: 加运算
  • sub: 减运算
  • mul: 乘运算
  • div: 除运算
  • mod: 求余运算
  • pow: 乘方

类的专有方法也支持重载。

11、实例
class Person:
    """人员信息"""
    # 姓名(共有属性)
    name = ''
    # 年龄(共有属性)
    age = 0
    def __init__(self, name='', age=0):
        self.name = name
        self.age = age
    # 重载专有方法: __str__
    def __str__(self):
        return "这里重载了 __str__ 专有方法, " + str({'name': self.name, 'age': self.age})
    def set_age(self, age):
        self.age = age
class Account:
    """账户信息"""
    # 账户余额(私有属性)
    __balance = 0
    # 所有账户总额
    __total_balance = 0
    # 获取账户余额
    # self 必须是方法的第一个参数
    def balance(self):
        return self.__balance
    # 增加账户余额
    def balance_add(self, cost):
        # self 访问的是本实例
        self.__balance += cost
        # self.__class__ 可以访问类
        self.__class__.__total_balance += cost
    # 类方法(用 @classmethod 标识,第一个参数为 cls)
    @classmethod
    def total_balance(cls):
        return cls.__total_balance
    # 静态方法(用 @staticmethod 标识,不需要类参数或实例参数)
    @staticmethod
    def exchange(a, b):
        return b, a
class Teacher(Person, Account):
    """教师"""
    # 班级名称
    _class_name = ''
    def __init__(self, name):
        # 第一种重载父类__init__()构造方法
        # super(子类,self).__init__(参数1,参数2,....)
        super(Teacher, self).__init__(name)
    def get_info(self):
        # 以字典的形式返回个人信息
        return {
            'name': self.name,  # 此处访问的是父类Person的属性值
            'age': self.age,
            'class_name': self._class_name,
            'balance': self.balance(),  # 此处调用的是子类重载过的方法
        }
    # 方法重载
    def balance(self):
        # Account.__balance 为私有属性,子类无法访问,所以父类提供方法进行访问
        return Account.balance(self) * 1.1
class Student(Person, Account):
    """学生"""
    _teacher_name = ''
    def __init__(self, name, age=18):
        # 第二种重载父类__init__()构造方法
        # 父类名称.__init__(self,参数1,参数2,...)
        Person.__init__(self, name, age)
    def get_info(self):
        # 以字典的形式返回个人信息
        return {
            'name': self.name,  # 此处访问的是父类Person的属性值
            'age': self.age,
            'teacher_name': self._teacher_name,
            'balance': self.balance(),
        }
# 教师 John
john = Teacher('John')
john.balance_add(20)
john.set_age(36)  # 子类的实例可以直接调用父类的方法
print("John's info:", john.get_info())
# 学生 Mary
mary = Student('Mary', 18)
mary.balance_add(18)
print("Mary's info:", mary.get_info())
# 学生 Fake
fake = Student('Fake')
fake.balance_add(30)
print("Fake's info", fake.get_info())
# 三种不同的方式调用静态方法
print("john.exchange('a', 'b'):", john.exchange('a', 'b'))
print('Teacher.exchange(1, 2)', Teacher.exchange(1, 2))
print('Account.exchange(10, 20):', Account.exchange(10, 20))
# 类方法、类属性
print('Account.total_balance():', Account.total_balance())
print('Teacher.total_balance():', Teacher.total_balance())
print('Student.total_balance():', Student.total_balance())
# 重载专有方法
print(fake)

输出:

John's info: {'name': 'John', 'age': 36, 'class_name': '', 'balance': 22.0}Mary's info: {'name': 'Mary', 'age': 18, 'teacher_name': '', 'balance': 18}Fake's info {'name': 'Fake', 'age': 18, 'teacher_name': '', 'balance': 30}john.exchange('a', 'b'): ('b', 'a')Teacher.exchange(1, 2) (2, 1)Account.exchange(10, 20): (20, 10)Account.total_balance(): 0Teacher.total_balance(): 20Student.total_balance(): 48这里重载了 __str__ 专有方法, {'name': 'Fake', 'age': 18}

三、错误和异常

1、语法错误

    SyntaxError 类表示语法错误,当解释器发现代码无法通过语法检查时会触发的错误。语法错误是无法用 try…except…捕获的。

>>>print:
>>> print:
  File "<stdin>", line 1
    print:
         ^
SyntaxError: invalid syntax
2、异常

    即便程序的语法是正确的,在运行它的时候,也有可能发生错误。运行时发生的错误被称为异常。 错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息。

>>> 1 + '0'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
3、异常处理

    Python 提供了 try … except … 的语法结构来捕获和处理异常。try 语句执行流程大致如下:

  • 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)
  • 如果没有异常发生,忽略 except 子句,try 子句执行后结束。
  • 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。最后执行 try 语句之后的代码。
  • 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。
  • 一个 try 语句可能包含多个 except 子句,分别来处理不同的特定的异常。
  • 最多只有一个 except 子句会被执行。
  • 处理程序将只针对对应的 try 子句中的异常进行处理,而不是其他的 try 的处理程序中的异常。
  • 一个 except 子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。
  • 最后一个 except 子句可以忽略异常的名称,它将被当作通配符使用。可以使用这种方法打印一个错误信息,然后再次把异常抛出。
  • try except 语句还有一个可选的 else 子句,如果使用这个子句,那么必须放在所有的 except 子句之后。这个子句将在try 子句没有发生任何异常的时候执行。
  • 异常处理并不仅仅处理那些直接发生在 try 子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常。
  • 不管 try 子句里面有没有发生异常,finally 子句都会执行。
  • 如果一个异常在 try 子句里(或者在 except 和 else 子句里)被抛出,而又没有任何的 except 把它截住,那么这个异常会在 finally 子句执行后再次被抛出。
4、抛出异常

    使用 raise 语句抛出一个指定的异常。

    raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

    如果你只想知道这是否抛出了一个异常,并不想去处理它,那么一个简单的 raise 语句就可以再次把它抛出。

5、自定义异常

    可以通过创建一个新的异常类来拥有自己的异常。异常类继承自 Exception 类,可以直接继承,或者间接继承。

    当创建一个模块有可能抛出多种不同的异常时,一种通常的做法是为这个包建立一个基础异常类,然后基于这个基础类为不同的错误情况创建不同的子类。

    大多数的异常的名字都以”Error”结尾,就跟标准的异常命名一样。

【实例】

import sys
class Error(Exception):
    """Base class for exceptions in this module."""
    pass
# 自定义异常
class InputError(Error):
    """Exception raised for errors in the input.
    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """
    def __init__(self, expression, message):
        self.expression = expression
        self.message = message
try:
    print('code start running...')
    raise InputError('input()', 'input error')
    # ValueError
    int('a')
    # TypeError
    s = 1 + 'a'
    dit = {'name': 'john'}
    # KeyError
    print(dit['1'])
except InputError as ex:
    print("InputError:", ex.message)
except TypeError as ex:
    print('TypeError:', ex.args)
    pass
except (KeyError, IndexError) as ex:
    """支持同时处理多个异常, 用括号放到元组里"""
    print(sys.exc_info())
except:
    """捕获其他未指定的异常"""
    print("Unexpected error:", sys.exc_info()[0])
    # raise 用于抛出异常
    raise RuntimeError('RuntimeError')
else:
    """当无任何异常时, 会执行 else 子句"""
    print('"else" 子句...')
finally:
    """无论有无异常, 均会执行 finally"""
    print('finally, ending')

四、文件操作

1、打开文件

    open() 函数用于打开/创建一个文件,并返回一个 file 对象:

opencv(filename, mode)
  • filename:包含了你要访问的文件名称的字符串值
  • mode:决定了打开文件的模式:只读,写入,追加等

文件打开模式

2、文件对象方法
  • fileObject.close()

    close() 方法用于关闭一个已打开的文件。关闭后的文件不能再进行读写操作,否则会触发 ValueError 错误。 close()方法允许调用多次。

    当 file 对象,被引用到操作另外一个文件时,Python 会自动关闭之前的 file 对象。 使用 close()方法关闭文件是一个好的习惯。

  • fileObject.flush()

    flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入。

    一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用 flush() 方法。

  • fileObject.fileno()

    fileno() 方法返回一个整型的文件描述符(file descriptor FD 整型),可用于底层操作系统的 I/O 操作。

  • fileObject.isatty()

    isatty() 方法检测文件是否连接到一个终端设备,如果是返回 True,否则返回 False。

  • next(iterator[,default])

    Python 3 中的 File 对象不支持 next() 方法。 Python 3 的内置函数 next() 通过迭代器调用 next() 方法返回下一项。在循环中,next() 函数会在每次循环中调用,该方法返回文件的下一行,如果到达结尾(EOF),则触发 StopIteration。

  • fileObject.read()

    read() 方法用于从文件读取指定的字节数,如果未给定或为负则读取所有。

  • fileObject.readline()

    readline() 方法用于从文件读取整行,包括 “” 字符。如果指定了一个非负数的参数,则返回指定大小的字节数,包括 “” 字符。

  • fileObject.readlines()

    readlines() 方法用于读取所有行(直到结束符 EOF)并返回列表,该列表可以由 Python 的 for… in … 结构进行处理。如果碰到结束符 EOF,则返回空字符串。

  • fileObject.seek(offset[, whence])

    seek() 方法用于移动文件读取指针到指定位置。

    whence 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾。whence 值为默认为0,即文件开头。例如:

    seek(x, 0):从起始位置即文件首行首字符开始移动 x 个字符

    seek(x, 1):表示从当前位置往后移动 x 个字符

    seek(-x, 2):表示从文件的结尾往前移动 x 个字符

  • fileObject.tell(offset[, whence])

    tell() 方法返回文件的当前位置,即文件指针当前位置。

  • fileObject.truncate([size])

    truncate() 方法用于从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后 V 后面的所有字符被删除,其中 Widnows 系统下的换行代表2个字符大小。

  • fileObject.write([str])

    write() 方法用于向文件中写入指定字符串。

    在文件关闭前或缓冲区刷新前,字符串内容存储在缓冲区中,这时你在文件中是看不到写入的内容的。

    如果文件打开模式带 b,那写入文件内容时,str (参数)要用 encode 方法转为 bytes 形式,否则报错:TypeError:
    a bytes-like object is required, not ‘str’。

  • fileObject.writelines([str])

    writelines() 方法用于向文件中写入一序列的字符串。这一序列字符串可以是由迭代对象产生的,如一个字符串列表。换行需要指定换行符 。
    【实例】

filename = 'data.log'
# 打开文件(a+ 追加读写模式)
# 用 with 关键字的方式打开文件,会自动关闭文件资源
with open(filename, 'w+', encoding='utf-8') as file:
    print('文件名称: {}'.format(file.name))
    print('文件编码: {}'.format(file.encoding))
    print('文件打开模式: {}'.format(file.mode))
    print('文件是否可读: {}'.format(file.readable()))
    print('文件是否可写: {}'.format(file.writable()))
    print('此时文件指针位置为: {}'.format(file.tell()))
    # 写入内容
    num = file.write("第一行内容")
    print('写入文件 {} 个字符'.format(num))
    # 文件指针在文件尾部,故无内容
    print(file.readline(), file.tell())
    # 改变文件指针到文件头部
    file.seek(0)
    # 改变文件指针后,读取到第一行内容
    print(file.readline(), file.tell())
    # 但文件指针的改变,却不会影响到写入的位置
    file.write('第二次写入的内容')
    # 文件指针又回到了文件尾
    print(file.readline(), file.tell())
    # file.read() 从当前文件指针位置读取指定长度的字符
    file.seek(0)
    print(file.read(9))
    # 按行分割文件,返回字符串列表
    file.seek(0)
    print(file.readlines())
    # 迭代文件对象,一行一个元素
    file.seek(0)
    for line in file:
        print(line, end='')
# 关闭文件资源
if not file.closed:
    file.close()

输出:

文件名称: data.log
文件编码: utf-8
文件打开模式: w+
文件是否可读: True
文件是否可写: True
此时文件指针位置为: 0
写入文件 6 个字符
 16
第一行内容
 16
 41
第一行内容
第二次
['第一行内容', '第二次写入的内容']
第一行内容
第二次写入的内容

五、序列化

    在 Python 中 pickle 模块实现对数据的序列化和反序列化。pickle 支持任何数据类型,包括内置数据类型、函数、类、对象等。

1、方法

dump:将数据对象序列化后写入文件

pickle.dump(obj, file, protocol=None, fix_imports=True)

    必填参数 obj 表示将要封装的对象。 必填参数 file 表示 obj 要写入的文件对象,file 必须以二进制可写模式打开,即wb。 可选参数 protocol 表示告知 pickle 使用的协议,支持的协议有 0,1,2,3,默认的协议是添加在 Python 3 中的协议3。

load:从文件中读取内容并反序列化

pickle.load(file, fix_imports=True, encoding='ASCII', errors='strict')

dumps:以字节对象形式返回封装的对象,不需要写入文件中

pickle.dumps(obj, protocol=None, fix_imports=True)

loads:从字节对象中读取被封装的对象,并返回

pickle.loads(bytes_object, fix_imports=True, encoding='ASCII', errors='strict')

【实例】

import pickle
data = [1, 2, 3]
# 序列化数据并以字节对象返回
dumps_obj = pickle.dumps(data)
print('pickle.dumps():', dumps_obj)
# 从字节对象中反序列化数据
loads_data = pickle.loads(dumps_obj)
print('pickle.loads():', loads_data)
filename = 'data.log'
# 序列化数据到文件中
with open(filename, 'wb') as file:
    pickle.dump(data, file)
# 从文件中加载并反序列化
with open(filename, 'rb') as file:
    load_data = pickle.load(file)
    print('pickle.load():', load_data)

输出

pickle.dumps(): b'€]q(KKKe.'
pickle.loads(): [1, 2, 3]
pickle.load(): [1, 2, 3]

六、命名规范化

    Python 之父 Guido 推荐的规范

猜你喜欢

转载自blog.csdn.net/sinat_33924041/article/details/84032904