python3进阶教程

python3进阶教程

第1章 Python面向对象编程

1 什么是面向对象编程

2 Python类的定义与实例化

Python中,通过class关键字定义一个类,比如我们需要定义一个人的类。按照 Python 的编程习惯,类名以大写字母开头。因此可以这样定义:

1、class Person:  pass
2、class Person(): pass 
3、class Person(object):  pass

这三种情况有什么区别呢?在Python3中,是没有区别的。
在Python2中,对于第一种定义的方法,Person类只有有限的几个内建函数’doc’, ‘module’, ‘name’,而对于第二种、第三种定义的方法,则会继承Python object对象的更多的内建函数。
定义了类之后,就可以对类进行实例化了
创建实例使用类名+(),类似函数调用的形式创建:

 class Person(object):  
 
 pass  #pass无论是前后都会执行相应的代码,单纯只有pass最后是没有输出出来,但也不会报错
#可以说在要执行else的时候,还不知道接下来做什么一个操作的时候,可以用pass来代替

 xiaohong = Person() 
 xiaoming = Person()

3 Python实例属性的定义

通过以下的方式赋予实例这些属性,并且把这些属性打印出来。

xiaohong.name = 'xiaohong'
xiaohong.sex = 'girl'
xiaohong.age = 13

print(xiaohong.name)
print(xiaohong.sex)
print(xiaohong.age)

4 Python实例属性的初始化

在定义 Person 类时,可以为Person类添加一个特殊的__init__()方法,当创建实例时,init()方法被自动调用,我们就能在此为每个实例都统一加上以下属性:

class Person(object):
    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

需要注意的是,init() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),后续参数则可以自由指定,和定义函数没有任何区别。
定义类后,就可以相应的实例化对象了,需要注意的是,在实例化的时候,需要提供除self以外的所有参数。

xiaoming = Person('Xiao Ming', 'boy', 13)
xiaohong = Person('Xiao Hong', 'girl', 14)

在定义__init__()方法常常忘记了 self 参数,这时候继续实例化就会报错;
实例化之后,当访问不存在的属性时,依然会报错

补充:Python的世界中,object是父子关系的顶端,所有的数据类型的父类都是它;type是类型实例关系的顶端,所有对象都是它的实例的。

5 Python类属性

含义:类和实例对象是有区别的,类是抽象,是模板,而实例则是根据类创建的对象,比如类:动物,只是一个抽象,并没有动物的详细信息,而猫、狗等,则是具体的动物,是类的对象。

定义类属性可以直接在 class 中定义,比如在前面的Animal类中,加入地域的类属性:

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age

在上面的代码中,localtion就是属于Animal这个类的类属性,此后,
通过Animal()实例化的所有对象,都可以访问到localtion,并且得到唯一的结果。

dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
print(dog.localtion) # ==> Asia
print(cat.localtion) # ==> Asia

#类属性,也可以通过类名直接访问
print(Animal.localtion) # ==> Asia

注意:
类属性也是可以动态添加和修改的,需要注意的是,因为类属性只有一份,所以改变了,所有实例可以访问到的类属性都会变更.

6 Python类属性和实例属性的优先级

如果类属性和实例属性名字相同时,会怎么样,这就涉及Python中类属性和实例属性的优先级的问题了。

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.localtion = localtion

#接着我们初始化两个实例,并把localtion打印出来。

​dog = Animal('wangwang', 1, 'GuangDong')
cat = Animal('mimi', 3, 'ChongQing')
print(dog.localtion) # ==> GuangDong
print(cat.localtion) # ==> ChongQing
print(Animal.localtion) # ==> Asia

可见,在类属性和实例属性同时存在的情况下,实例属性的优先级是要高于类属性的,在操作实例的时候,优先是操作实例的属性。

另外,通过实例,可不可以修改类属性呢?我们来尝试一下:

以上面代码例子:
localtion = 'Asia'

cat.localtion = 'Africa'
print(Animal.localtion) # ==> Asia

这里依然打印了Asia,可见通过实例是无法修改类的属性的,事实上,
通过实例方法修改类属性,只是给实例绑定了一个对应的实例属性:

​# 增加的实例属性
print(cat.localtion) # ==> Africa

因此,需要特别注意,尽量不要通过实例来修改类属性,否则很容易引发意想不到的错误。



如果存在类属性,而不存在实例属性,你想通过实力修改类属性,编译时就会报错,
会让你意识到自己操作有问题,不会导致太大的问题;

如果类属性和实例属性名字相同,你想通过实例修改类属性,实际改的是实例属性,
就造成了意想不到的错误;

7 Python中的访问限制

并不是所有的属性都可以被外部访问的,这种不能被外部访问的属性称为私有属性。私有属性是以双下划线’__'开头的属性。

# 类私有属性
class Animal(object):
    __localtion = 'Asia'

print(Animal.__localtion)

报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Animal' has no attribute '__localtion'

实例私有属性:

class Animal(object):
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.__localtion = localtion

dog = Animal('wangwang', 1, 'GuangDong')
print(dog.name) # ==> wangwang
print(dog.age) # ==> 1
print(dog.__localtion)


Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute '__localtion'

在外部访问私有属性将会抛出异常,提示没有这个属性。
虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。私有属性是为了保护类或实例属性不被外部污染而设计的。

8 Python定义实例方法

私有属性没有办法从外部访问,只能在类的内部操作;那如果外部需要操作私有属性怎么办?
这个时候可以通过定义类或者实例的方法来操作私有属性

先来介绍实例方法。
实例的方法指的就是在类中定义的函数,实例方法的第一个参数永远都是self,self是一个引用,指向调用该方法的实例对象本身,除此以外,其他参数和普通函数是完全一样的。

class Person(object):

    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

在上面的定义,name是实例的私有属性,从外部是无法访问的,而get_name(self) 就是一个实例方法,在实例方法里面是可以操作私有属性的,注意,它的第一个参数是self。
另外,__ init__(self, name)其实也可看做是一个特殊的实例方法。
通过定义get_name(self)方法,在外部就可以通过这个方法访问私有属性了。
例如:

​p = Person('Alice')
print(p.get_name()) # ==> Alice

当然,实例方法并不仅仅是为私有属性服务的,我们可以把和类的实例有关的操作都抽象成实例方法,比如:打印实例的详细信息等等。

class Animal(object):
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.localtion = localtion

    def get_info(self):
        return 'name = {}, age = {}, localtion = {}'.format(self.name, self.age, self.localtion)

dog = Animal('wangwang', 1, 'GuangDong')
print(dog.get_info())

任务

把Animal类的age、name、localtion定义成私有属性,并定义对应的方法修改和获取他们的值。

    class Animal(object):
        def __init__(self, name, age, localtion):
            self.__name = name
            self.__age = age
            self.__localtion = localtion

        def set_name(self, name):
            self.__name = name
		#定义set方法为了修改私有属性的值。也可以在get_name()中添加相应的代码实现。

        def get_name(self):#get方法获取
            return self.__name

        def set_age(self, age):
            self.__age = age

        def get_age(self):
            return self.__age

        def set_localtion(self, localtion):
            self.__localtion =localtion

        def get_localtion(self):
            return self.__localtion

9 Python定义类方法

为了操作实例对象的私有属性,我们定义了实例方法;同样的,如果需要需要操作类的私有属性,则应该定义类的方法。
默认的,在class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。
要在class中定义类方法,需要这么写:

class Animal(object):
    __localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def set_localtion(cls, localtion):
        cls.__localtion = localtion

    @classmethod
    def get_localtion(cls):
        return cls.__localtion

print(Animal.get_localtion()) # ==> Asia
Animal.set_localtion('Afica')
print(Animal.get_localtion()) # ==> Africa

注意:
类方法需要使用@classmethod来标记为类方法,否则定义的还是实例方法
 类方法的第一个参数将传入类本身,通常将参数名命名为 cls,
 上面的 cls.__localtion 实际上相当于Animal.__localtion。

因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用。

第2章 Python类的继承

1 什么是类的继承

2 Python继承类

对人类的抽象可以定义为Person类,而学生、老师等,也都是人类,所以,在Python当中,如果定义学生Student的类,可以继承Person类。

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

接着定义Student类,在定义Student类的时候,由于继承了Person类,所以Student类自动拥有name、gender属性,因此,在定义Student类的时候,只需要把额外的属性加上即可。

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

student = Student('Alice', 'girl', 100)
print(student.name) # ==> Alice
print(student.gender) # ==> girl
print(student.score) # ==> 100

在定义继承类的时候,需要注意的:

    class Student()定义的时候,需要在括号内写明继承的类Person
    在__init__()方法,需要调用super(Student, self).__init__(name, gender),
    来初始化从父类继承过来的属性

3 Python判断类型

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

当我们拿到变量 p、s、t 时,可以使用 isinstance 判断类型:

>>> isinstance(p, Person)
True # p是Person类型
>>> isinstance(p, Student)
False # p不是Student类型
>>> isinstance(p, Teacher)
False # p不是Teacher类型

我们再考察 s:

>>> isinstance(s, Person)
True # s是Person类型
>>> isinstance(s, Student)
True # s是Student类型
>>> isinstance(s, Teacher)
False # s不是Teacher类型

s 是Student类型,不是Teacher类型,这很容易理解。但是,s 也是Person类型,
因为Student继承自Person,虽然它比Person多了一些属性和方法,但是,
把 s 看成Person的实例也是可以的。

这说明在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型。

isinstance也可以用于Python自有数据类型的判断。

​s = 'this is a string.'
n = 10
isinstance(s, int) # ==> False
isinstance(n, str) # ==> False

4 Python中的多态

如果我们从 Person 派生出 Student和Teacher ,并都写了一个who() 方法:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is %s' % self.name

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
    def who(self):
        return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course
    def who(self):
        return 'I am a Teacher, my name is %s' % self.name

接着,我们分别把不同类型的who()函数结果打印出来:

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

运行结果:

I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice

这种行为称为多态。从定义上来讲,Student和Teacher都拥有来自父类Person继承的who()方法,以及自己定义的who()方法。但是在实际调用的时候,会首先查找自身的定义,如果自身有定义,则优先使用自己定义的函数;如果没有定义,则顺着继承链向上找。

class Boss(Person):
    def __init__(self, name, gender,company):
        super(Boss, self).__init__(name, gender)
        self.company = company

b = Boss('Bob', 'Male', 'Alibaba')
b.who() # ==> I am a Person, my name is Bob

在Boss的定义类,没有定义who方法,所以会顺着继承链向上找到父类的who方法并且调用。

Python中的多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。多重继承和单继承没有特别大的差异,只是在括号内加入多个需要继承的类的名字即可。

class A(object):
    def __init__(self, a):
        print ('init A...')
        self.a = a

class B(A):
    def __init__(self, a):
        super(B, self).__init__(a)
        print ('init B...')

class C(A):
    def __init__(self, a):
        super(C, self).__init__(a)
        print ('init C...')

class D(B, C):
    def __init__(self, a):
        super(D, self).__init__(a)
        print ('init D...')

多重继承的继承链就不是一棵树了,它像这样:

父类A
子类B继承A
子类C继承A
子类D继承BC

从上图可知,A类被继承了连词,那么A的__init__()方法,是否会被调用两次呢?

​>>> d = D('d')
init A...
init C...
init B...
init D...

实践证明,在多重继承里,A虽然被继承了两次,但是__init__()的方法只调用一次。
多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。

5 Python获取对象信息

通过type()函数,可以获得变量的类型。

n = 1
s = 'this is a string'
type(n) # ==> <class 'int'>
type(s) # ==> <class 'str'>

 

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

p = Person('Alice', 'Female')
s = Student('Bob', 'Male', 100)
type(p) # ==> <class '__main__.Person'>
type(s) # ==> <class '__main__.Student'>

通过dir()方法,可以获取变量的所有属性:

>>> n = 1
>>> dir(n)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', ...]
>>> s = 'this is a string'
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', ...]

在dir列出的属性中,有很多是以下划线开头和结尾的,这些都是特殊的方法,称为内建方法。
对于自定义对象:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is {}'.format(self.name)

p = Person('Alice', 'Female')
dir(p)

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'who']

对于实例变量,dir()返回所有实例属性,包括__class__这类有特殊意义的属性。注意到方法who也是p的一个属性。

dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() 和 setattr( )函数了。

>>> getattr(p, 'name') # 获取name属性
'Alice'
>>> setattr(p, 'name', 'Adam') # 设置新的name属性
>>> s.name
'Adam'
>>> getattr(s, 'age') # 获取age属性,但是属性不存在,报错:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'
>>> getattr(s, 'age', 20) # 获取age属性,如果属性不存在,就返回默认值2020

第3章 Python类的特殊方法

1 什么是类的特殊方法

双下划线开头
双下划线结束
例如:__str__()

2 Python类的__str__ 和 __repr__方法

Python的内建对象,比如int、dict、list等,通过str()方法,可以转换成字符串输出。

num = 12
str(num) # ==> '12'
d = {1: 1, 2: 2}
str(d) # ==> '{1: 1, 2: 2}'
l = [1,2,3,4,5]
str(l) # ==> '[1, 2, 3, 4, 5]'

自定义的对象,通过str()方法,同样可以得到对象所对应的字符串结果,只不过结果会有些难理解。

class Person:
    pass

bob = Person()
str(bob) # ==> '<__main__.Person object at 0x7fc77b859c50>'

<__main__.Person object at 0x7fc77b859c50>这个结果其实是Animal的实例cat在内存中的地址,
这是相当难以理解的
这其实是对象的内建方法__str__返回的。

str()函数似乎没有在自定义类Person中生效,这是为什么呢?
这是因为 Python 定义了__str()和__repr()两种方法,str()用于显示给用户,而__repr()用于显示给开发人员。
**==当使用str()时,实际调用的是
str ()方法,而直接输入变量,调用的是 repr __()方法。==**

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def __str__(self):
        return 'name: {}, gender: {}'.format(self.name, self.gender)
    def __repr__(self):
        return 'name: {}, gender: {}'.format(self.name, self.gender)

bob = Person('Bob', 'Male')
str(bob) # ==> 'name: Bob, gender: Male'
>>> bob
'name: Bob, gender: Male'

3 Python类的__len__方法

如果一个类表现得像一个list,想使用len()函数来获取元素个数时,则需要实现__len__()方法。

class Class:
    def __init__(self, students):
        self.students = students
    def __len__(self):
        return len(self.students)

students = ['Alice', 'Bob', 'Candy']
class_ = Class(students)
len(class_) # ==> 3

通过自定义__len__()方法,可以让len()函数返回相关的结果,如果没有定义__len__()方法的类使用len()函数获取长度时,将会引起异常。

4 Python类的数学运算

ython很多的操作都是通过内建函数来实现的,比如最熟悉的加减乘除,都是通过内建函数来实现的,分别是__add__、submultruediv

5 Python类的__slots__方法

例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的__slots__来实现。

class Student(object):
    __slots__ = ('name', 'gender', 'score')
    def __init__(self, name, gender, score):
        self.name = name
        self.gender = gender
        self.score = score

使用__slots__ = (‘name’, ‘gender’, ‘score’) 限定Student类的属性,这个时候在外部再次添加动态属性age,将会报错。

6 Python类的__call__方法

在Python中,函数其实是一个对象,我们可以将一个函数赋值给一个变量,而不改变函数的功能。
如果把一个类实例也变成一个可调用对象,可以实现一个特殊的方法__call__()。
例如,我们把Person类变成一个可调用对象:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __call__(self, friend):
        print('My name is {}...'.format(self.name))
        print('My friend is {}...'.format(friend))

接着我们初始化一个Person对象,并对这个对象通过函数的方式调用:

>>> p = Person('Bob', 'Male')
>>> p('Alice') # ==> 用函数的方式调用Person类的实例p
My name is Bob...
My friend is Alice...

第4章 Python的模块

1 Python模块和包

#main.py //自身模块
import tools  //导入自定义tools.py模块
或
from network import tools  //导入network包的tools.py模块

print(tools.say_hello())//调用tools.py模块函数

python包的文件夹必须有__init__.py文件

2 Python定义模块

# tools.py
def say_hello():
    print('hello')

def say_goodbye():
    print('goodbye')

这样就定义了一个叫tools的模块,接着就可以使用这个模块了

3 Python导入模块

Python使用import语句导入一个模块,Python官方提供很多有用的模块,比如:os模块、sys模块、time模块、math模块等等。
导入官方模块,不需要考虑路径的问题,例如,导入系统自带的模块 math,直接导入即可。如果是导入自定义模块,则需要考虑路径问题。

如果希望导入模块的指定部分属性或函数,那么使用from…import…语句。

>>> from math import pi
>>> print(pi)
3.141592653589793

由于pow()函数没有导入,所以是不能使用的
如果希望导入模块里面的所有内容,那么使用from …import *语句。

>>> from math import *
>>> print(pi)
3.141592653589793
>>> pow(2, 3)
8.0

有时候导入模块函数名字重复会发生冲突,两种解决办法:
第一种是直接导入模块,不指定导入模块里面的具体内容;第二种方法就是使用from … import as …语句,as类似重命名,可以把导入的函数或属性重命名为别的名字。

>>> from math import pow as mathpow
>>> mathpow(2, 3)
8.0

4 Python模块导入的路径

如果需要导入自定义模块,则需要了解Python导入模块搜索的路径。

通过sys模块,可以知道导入模块的路径。

>>> import sys
>>> sys.path
['', '/data/miniconda3/lib/python3.8', '/data/miniconda3/lib/python3.8/site-packages']

它返回的是一个列表,表示的是在搜索Python模块时,会搜索的路径,在示例中,返回了四个路径。我们分析一些关键路径:
第一个路径是'',它是一个空字符串,表达的是当前路径的意思。
第二个路径是/data/miniconda3/lib/python3.8,它是Python默认模块的存放的路径,在这个路径下,可以发现有os、sys等模块的代码。
第三个路径是/data/miniconda3/lib/python3.8/site-packages,它是第三方模块代码的存放路径,在这个路径下,存放的是需要安装的第三方模块。

那如何使用我们前面定义的tools.py模块呢?
我们在tools.py同级目录,创建main.py文件:

# main.py

import tools # 导入模块

tools.say_hello() # 调用模块里面的say_hello()函数
tools.say_goodbye() # 调用模块里面的say_goodbye()函数

在同一个目录内的tools.py模块,可以被搜索到,所以能够import进来。

Python的sys.path返回的是一个路径列表,因此可以操作列表里面的元素,
通过sys.path增加路径’…/’,使得在运行时,可以导入当前目录上级目录的包。

import sys
sys.path.append('../')

5 Python安装第三方模块的方法

尽管Python的官方模块已经提供了非常强大的能力,但是仍有大量热心开发者提供了非常好用的第三方库,在实际开发中,也会经常使用,比如Web开发框架,Django、Flask,异步任务框架:Celery等。
在安装Python的时候,Python环境提供了安装第三方模块的工具:pip,通过这个工具,可以非常快捷的安装第三方模块。
安装Django模块:

pip install django

卸载Django模块:

pip uninstall django

第5章 Python读写文件

1 向Python程序输入内容

使用input()函数可以接收外部的输入。

注意:input输入的是字符串,需要转型为数字类型。

num = input('please input number: ')

num = int(num)
result = 1
for i in range(1, num):
    result = result * i

print(result)

eval()函数可以把字符串转换为等值的结果,比如eval(‘1+1’),得到结果为2。

2 Python打开文本文件

open()函数,可以打开一个文件,得到一个文件file对象,而file对象提供相关的方法对文件内容进行读写等操作。

    文件路径:指定需要打开的文件的文件路径
    打开模式:针对不同文件(二进制文件、文本文件)以及不同操作(读操作、写操作),会有不同的打开模式
    编码:设定打开文件的默认编码

常用的打开模式如下:

模式描述
t文本模式(默认)
x写模式,新建一个文件
b二进制模式,打开二进制文件
+更新一个文件(可读可写)
r以只读模式打开一个文件
rb以二进制格式只读模式打开一个文件
w打开一个文件进行写入,如果文件内容已存在,会清除原有的内容
wb以二进制格式只写模式打开一个文件,会清除原有的内容
a打开一个文件并追加内容,会往文件尾部添加内容
ab以二进制格式打开一个文件并追加内容,会往文件尾部添加内容
w+打开一个文件进行读写,如果文件内容已存在,会清除原有的内容
a+打开一个文件并使用追加进行读写

文件使用完毕后,需要使用close()函数正确关闭。

3 Python打开二进制文件

非文本文件,比如图片、压缩文件、视频文件、音乐文件等等,这种文件统称为二进制文件。
用只读方式打开二进制文件:

f = open('test.jpg', 'rb')
f.close()

4 Python读取文件内容

read()方法,可以读取文件中的若干个字符,它提供一个参数size,可以指定读取字符的数量。
比如打开了一个文件:

f = open('test.txt', 'r') # 打开test.txt文件
f.close() # 关闭文件

test.txt文件有以下内容
Hello World.
Hello Python.
Hello Imooc.

使用read读取一下
s = f.read(5)
print(s) # ==> Hello

当read()之后,访问文件的游标就会移动到第六个字符前面,
此时,继续read,将得到Hello后面的结果。

s = f.read(6)
print(s) # ==> ' World'

读取一行

readline()方法,和read()方法类似,可以读取文件中的若干个字符,它也提供一个参数size,可以指定读取字符的数量,不过和read()方法不同的是,readline()方法遇到一行结束的时候,就会返回。

f.close()
f = open('test.txt', 'r') # 重新打开文件
s = f.readline(20)
print(s)  # ==> 'Hello World.\n'

可以看到,打印的内容并没有20个字符,readline最多返回一行的所有字符。

读取多行

readlines()方法,可以读取多行字符,返回一个列表。它提供一个hint参数,表示指定读取的行数,没有指定则默认以列表的形式返回文件所有的字符串。

f.close()
f.open('test.txt', 'r')
s = f.readlines()
print(s) # ==> ['Hello World.\n', 'Hello Python.\n', 'Hello Imooc.\n']

5 Python把字符串写入文件

写入若干字符

write方法向文件内写入若干字符,它接受一个字符串参数,表示需要写入的字符串。

f = open('test.txt', 'w')
f.write('Hello World\n')
f.close()

写入若干行

writelines()方法向文件内容写入多行数据,它接受一个列表,表示需要写入的字符串列表。

lines = ['Hello World\n', 'Hello Python\n', 'Hello Imooc\n']
f = open('test.txt', 'w')

f.writelines(lines)
f.close()

reverse()数据反转:

是python中列表的一个内置方法(也就是说,在字典,字符串或者元组中,是没有这个内置方法的),用于列表中数据的反转;

    lista = [1, 2, 3, 4]
    lista.reverse()
    print(lista)

打印结果:
    [4, 3, 2, 1]

6 Python往文件追加内容

通过w的打开方式打开文件,会清空文件的内容,这在很多场景下是不合适的,比如写系统日志的时候,需要累积随时间推移的所有数据。

使用a的打开方式打开文件,文件游标默认是在文件的尾部,因此,可以便捷的往文件尾部添加内容,除此以外,文件对象还提供seek()方法,可以移动文件的游标位置,它接受一个参数,表示文件的位置,0:文件首部,1:当前位置,2:文件尾部,通过seek()可以把文件游标移动到文件首部但不删除文件的内容。

​f = open('test.txt', 'a+')
content = f.readlines()
print(content) # ==> []
f.seek(0)
content = f.readlines()
print(content) # ==> ['Hello World\n', 'Hello Python\n', 'Hello Imooc\n']

第一次print(content)的时候,由于文件游标在文件的尾部,所以readlines()读取不到任何数据,打印了空的结果,第二次print(content)的时候,由于通过seek(0),文件游标移动到了文件的首部,因此readlines()就返回了文件所有的内容。

7 Python正确关闭文件

close()方法,也不是100%安全的,如果在close()文件之前,程序异常退出了,那么文件也得不到正确的关闭。

with关键字可以解决这个问题
with关键字对资源进行访问的场合,会确保不管在使用过程中是否发生异常,都会执行必要的“清理”的操作,释放资源,比如文件使用后自动关闭等等。
with的使用方法如下:

with open('test.txt', 'r') as f:
    content = f.readlines()
    for line in content:
        print(line)

当文件使用结束后,不需要显式的调用f.close()关闭文件。

第6章 Python的网络编程

1 什么是网络编程

要进行网络通信,需要建立起通信双方的连接,连接的双方分别称为客户端和服务端。
在Python中,使用套接字socket来建立起网络连接。
套接字包含在socket模块中:

import socket
socket.socket()

2 套接字Socket与套接字编程

服务端建立需要四个步骤:新建socket、绑定IP和端口(bind)、监听连接(listen)、接受连接(accept)。
客户端建立则简单一些,仅需两个步骤:新建socket、连接服务端(connect)。

当网络连接上以后,客户端和服务端就可以进行数据通信了,套接字通过send()函数发送数据,通过recv()函数接收数据。
服务端的过程,新建一个server.py的文件:

import socket

server = socket.socket() # 1. 新建socket
server.bind(('127.0.0.1', 8999)) # 2. 绑定IP和端口(其中127.0.0.1为本机回环IP)
server.listen(5) # 3. 监听连接
s, addr = server.accept() # 4. 接受连接
print('connect addr:{}'.format(addr))
content =s.recv(1024)
print(str(content, encoding='utf-8'))  # 接受来自客户端的消息,并编码打印出来
s.close()

客户端,新建一个client.py的文件:

import socket

client = socket.socket() # 1. 新建socket
client.connect(('127.0.0.1', 8999)) # 2. 连接服务端(注意,IP和端口要和服务端一致)
client.send(bytes('Hello World. Hello Socket', encoding='utf-8')) # 发送内容,注意发送的是字节字符串。
client.close()

接着在两个终端运行这两个文件,成功之后在服务端的终端,将会输出以下信息:

connect addr:('127.0.0.1', 50382)
b'Hello World. Hello Socket'

3 Python自带的HTTP服务器

4 Python发送HTTP请求

除了使用浏览器发送HTTP请求,通过代码也可以向HTTP服务器发送请求,Python提供了相关的库urllib,通过urllib包里面的request,可以向其他HTTP服务器发起请求。

from urllib import request
response = request.urlopen('https://www.imooc.com') # 向慕课网官网发出请求
print(response) # ==> <http.client.HTTPResponse object at 0x000001377D631948>

这是HTTPResponse附带的一些信息,包括服务端的服务器是什么、请求时间、内容类型、内容长度等等。

Server: openresty //服务端的服务器
Date: Thu, 20 Aug 2020 08:16:07 GMT //请求时间
Content-Type: text/html; charset=UTF-8 //内容类型
Content-Length: 376639 //内容长度

5 Python的requests库

requests库是著名的Python第三方库,使用requests库,可以定制化你的HTTP请求,包括请求方法,请求参数等等。
由于requests是第三方库,因此在使用前,需要安装。

pip install requests

安装完成后,使用requests库来请求慕课网官网。

response = requests.get('https://www.imooc.com')

# 打印状态码
print(response.status_code)
# 打印回应头
print(response.headers)

6 HTTP响应的内容

网络爬虫是典型的应用程序,它的工作原理就是通过不断的请求互联网的页面,并从回应中解析获取出有用的数据;数据积累后,可以有很多用处。
过滤出来的信息或许有些杂乱,但也包含了一些有用的数据,我们可以从过滤后的信息中找到链接相关的信息。不过我们过滤的方式比较简单,通过优化过滤的方式可以匹配到更加精准的数据。而爬虫正是这样工作的。

第7章 Python的函数式编程

1 什么是函数式编程

2 Python把函数作为参数

计算平方根可以利用math包的sqrt()函数。
    import math

    def add(x, y, f):  //参数 x, y 和 f 都可以任意传入,如果 f 传入其他函数,就可以得到不同的返回值。
        return f(x) + f(y)

    print(add(25, 9, math.sqrt)) //计算25和9开方之后的和,结果为8

3 Python的map()函数

map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f依次作用在list的每个元素上,map()函数会返回一个迭代器,可以依次迭代得到原来list的元素被函数f处理后的结果。

传入函数f(x)=x*x,就可以利用map()函数完成这个计算:

def f(x):
    return x*x

for item in map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]):
    print(item)

得到结果:

[1, 4, 9, 10, 25, 36, 49, 64, 81]

4 Python的reduce()函数

reduce()函数被收录到functools包内,需要引入functools才可以使用。
例如,编写一个f函数,接收x和y,返回x和y的和:

def f(x, y):
    return x + y

#调用 reduce(f, [1, 3, 5, 7, 9]):

from functools import reduce

def f(x, y):
    return x + y

print(reduce(f, [1,3,5,7,9])) # ==> 25
#计算过程
先计算头两个元素:f(1, 3),结果为4;
再把结果和第3个元素计算:f(4, 5),结果为9;
再把结果和第4个元素计算:f(9, 7),结果为16;
再把结果和第5个元素计算:f(16, 9),结果为25;
由于没有更多的元素了,计算结束,返回结果25。

5 Python的filter()函数

利用filter()函数,可以完成很多很有用的功能,例如,删除 None 或者空字符串:

def is_not_empty(s):
    return s and len(s.strip()) > 0

for item in filter(is_not_empty, ['test', None, '', 'str', '  ', 'END']):
    print(item)

结果:test, str, END
注意: s.strip()会默认删除空白字符(包括'\n', '\r', '\t', ' '),如下:

s = '     123'
s.strip() # ==> 123
s= '\t\t123\r\n'
s.strip() # ==> 123

6 Python自定义排序函数

Python内置的 sorted()函数可对list进行排序:默认是由小到大排序

>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

当list的每一个元素又是一个容器时,则会以第一个元素来排序,比如在score中,每个元素都是包含名字和成绩的一个tuple,sorted()函数则按名字首字母进行了排序并返回。

>>> score = [('Alice', 72), ('Candy', 90), ('Bob', 62)]
>>> sorted(score)
[('Alice', 72), ('Bob', 62), ('Candy', 90)]sorted()函数按照成绩来进行排序:

def k(item):
    return item[1] # ==> 按成绩排序,成绩是第二个字段
sorted(score, key=k)

得到结果:[('Bob', 62), ('Alice', 72), ('Candy', 90)] 。

如果需要倒序,指定reverse参数即可。

sorted(score, key=k, reverse=True)

得到结果:[('Candy', 90), ('Alice', 72), ('Bob', 62)]

7 Python返回函数

def f():
    print('call f()...')
    # 定义函数g:
    def g():
        print('call g()...')
    # 返回函数g:
    return g

>>> x = f()   # 调用f()
call f()...
>>> x   # 变量x是f()返回的函数:
<function f.<locals>.g at 0x7f4a4936dbf8>
>>> x()   # x指向函数,因此可以调用
call g()...   # 调用x()就是执行g()函数定义的代码

有必要注意的是,返回函数和返回函数值的语句是非常类似的,返回函数时,不能带小括号,而返回函数值时,则需要带上小括号以调用函数。

8 Python的闭包

将g的定义移入函数 f 内部,防止其他代码调用 g:

def f():
    print('f()...')
    def g():
        print('g()...')
    return g

像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。

闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:

# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果全部都是 9
正确例子:

def count():
    fs = []
    for i in range(1, 4):
        def f(j):
            def g():
                return j*j
            return g
        r = f(i)
        fs.append(r)
    return fs
f1, f2, f3 = count()
print(f1(), f2(), f3())

9 Python的匿名函数

匿名函数使用lambda定义:lambda x: x * x,就可以完成原来显式定义的f(x)函数的功能,冒号前面的x表示匿名函数的参数,后面的是一个表达式,匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。

result = [item for item in map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])]
print(result) # ==> [1, 4, 9, 16, 25, 36, 49, 64, 81]

10 Python编写无参数的decorator

Python的 decorator 本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数。
使用 decorator 用Python提供的 @ 语法,这样可以避免手动编写 f = decorate(f) 这样的代码。

@log 是Python特有的一种函数简写方式,相当于factorial=log(factorial),将factorial()函数作为log函数的参数进行调用
@log
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print(factorial(10))

结果:

call factorial()...
3628800

@log 写死了只含一个参数的返回函数。不是一个参数调用会报错

11 Python编写有参数的decorator

12 Python的偏函数

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值