廖雪峰Python教程学习笔记(5)

8. 模块

在 Python 中,一个 .py 文件就称之为一个模块(Module)。

模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块所使用。

模块命名不要和系统模块名冲突,在 Python 交互式环境中,import abc,若成功说明存在此名称的系统模块;否则,不存在。

8.1 使用模块

sys模块是 Python 内置的一个模块。

使用 sys 模块的第一步,就是导入该模块:

import sys

导入 sys 模块后,就有了 sys 变量指向该模块,这样通过 sys 变量,就可以访问 sys 模块的所有功能。

作用域

正常的函数和变量名是公开的(public),可以被直接引用,比如 abc, PI等;

类似__xxx__ 这样的变量是特殊变量,可以被直接引用,但是有特殊的用途,比如 __author____name__ 就是特殊变量,自己一般不要这样命名;

类似_xxx__xxx 这样的函数或变量就是非公开的(private),不应该被直接引用,如 _abc, __abc 等。

_xxx 这样的函数或变量是可以被外界访问到的。但是,按照约定俗成的规定,这样的函数或变量表示的含义是“虽然我可以被访问,但是请把我视为私有变量,不要随意访问”。

8.2 安装第三方模块

在 Python 中,安装第三方模块,是通过包管理工具 pip 完成的。

第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索。
比如安装 Pillow,命令是:

pip install Pillow

安装常用模块

使用Anaconda,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库。Anaconda 自带 Python。

9. 面向对象编程

在 Python 中,所有数据类型都可以视为对象,也可以自定义对象。

9.1 类和实例

在 Python 中,通过 class 关键字来定义类,如 Student 类:

class Student(object):
    pass

class 关键字后面跟着的 Student 是类名,类名通常以大写字母开头,这点和 Java 是一致的。

紧接着类名的是(object),表示该类是从哪个类继承下来的。通常,如果没有合适的继承类,就用 object类。object类是所有类最终都会继承的类。

pass 是空语句,一般用作占位语句,是为了保证程序结构的完整性。如果不加的话,会抛出异常:IndentationError: expected an indented block。之前的笔记里提到过。这里算是复习了。

使用 Student类创建实例:

bart = Student()
print(bart) # <__main__.Student object at 0x7f64b90fc898>
print(Student) # <class '__main__.Student'>

从打印信息可以看出,bart 指向的是一个 Student 的实例,它有内存地址;而Student 本身是一个类。

可以自由地给一个实例绑定属性

这一点真的很新鲜,它是怎么做到的?

bart.name = 'Bart Smith'
print(bart.name) # Bart Smith
bart.age = 32
print(bart.age) # 32
bart.salary = 20000.0
print(bart.salary) # 20000.0

把必须绑定的属性强制写进去

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

__init__ 方法的作用:通过一个特殊的 __init__ 方法,在创建实例的时候把 namescore等属性绑定上去。

__init__是一个特殊的方法,它的第一个参数永远是 self,表示创建的实例本身。

__init__ 方法内部,把各种属性绑定到 self

使用 Student 类创建实例:

# bart = Student() # 报错:TypeError: __init__() missing 2 required positional arguments: 'name' and 'score'
bart = Student('Bart Smith', 0)
print(bart) # <__main__.Student object at 0x7fad2fbf2b00>
print('name = %s, score = %s' % (bart.name, bart.score)) # name = Bart Smith, score = 0

注意到现在使用 Student() 这种方式直接报错,这是因为我们在类中使用了 __init__ 方法,就不能再传入空参数了,必须传入与 __init__相匹配的参数,但 self 是个例外,我们不需要传 self,Python 解释器会自己把实例变量 self 传进去。因此,需要传入的是 namescore

普通函数和类中定义的函数有什么区别?

区别只有一个,就是在类中定义的函数,它的第一个参数永远是实例变量 self,而且,在调用时,不用传递该参数。

数据封装

在类中定义方法,通过在实例上调用方法,就可以直接操作对象内部的数据,不需要知道方法内部的实现细节。

class Student(object):

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

    # 定义一个类的方法
    def print_score(self):
        print('%s: %s' % (self.name, self.score))

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

# 使用 Student 类
bart = Student('Peter Wang', 59)
bart.print_score() # 打印结果:Peter Wang: 59
print(bart.get_grade()) # C

特别注意的一点

和静态语言不同,Python 允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称可能不同。

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    
# 对不同的实例变量绑定不同的数据
wang = Student('zhichao', 100)
li = Student('xiaolong', 100)
wang.age = 18
li.height = 176
print(wang.age) # 打印:18
# print(li.age) # 此行报错
'''
Traceback (most recent call last):
  File "student4.py", line 12, in <module>
    print(li.age)
AttributeError: 'Student' object has no attribute 'age'
'''
print(li.height) # 打印:176
# print(wang.height) # 此行报错
'''
Traceback (most recent call last):
  File "student4.py", line 20, in <module>
    print(wang.height)
AttributeError: 'Student' object has no attribute 'height'
'''

9.2 访问限制

隐藏变量,对外暴露 getter/setter 方法

# 增加对属性的访问限制, 暴露出访问属性的方法
class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    
    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

    # getter/setter 方法
    def get_name(self):
        return self.__name
    
    def set_name(self, name):
        self.__name = name

    def get_score(self):
        return self.__score

    def set_score(self, score):
        if score >= 0 and score <= 100:
            self.__score = score
        else:
            raise ValueError('illegal score')

wang = Student('zhichao', 100)
# print(wang.__name) # 此行报错
'''
Traceback (most recent call last):
  File "student6_access.py", line 12, in <module>
    print(wang.__name)
AttributeError: 'Student' object has no attribute '__name'
'''
print('%s: %s' % (wang.get_name(), wang.get_score())) # 打印:zhichao: 100
wang.set_name('zhijie')
# wang.set_score(150) # 不合法的分数,抛异常
wang.set_score(99)
print('%s: %d' % (wang.get_name(), wang.get_score())) # 打印:zhijie: 99

9.3 继承和多态

继承

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass
class Cat(Animal):
    pass

dog = Dog()
dog.run() # 打印:Animal is running...

cat = Cat()
cat.run() # 打印: Animal is running...

上面的代码里,Dog 类 继承了 Animal 类,写法是把 Animal 写在 Dog 后面的圆括号里面。这时,我们说 Dog 类是 Animal 类的子类,Animal 类是 Dog 类的基类、父类或超类。

通过继承,子类获取了父类的全部功能。

子类还可以增加一些方法。

多态

# 多态
class Animal(object):

    def run(self):
        print('Animal is running...')
    
class Dog(Animal):

    def run(self):
        print('Dog is running...')

class Cat(Animal):

    def run(self):
        print('Cat is running...')

def run_twice(animal):
    animal.run()
    animal.run()


run_twice(Animal())
run_twice(Dog())
run_twice(Cat())

'''
Animal is running...
Animal is running...
Dog is running...
Dog is running...
Cat is running...
Cat is running...
'''

多态的含义:父类引用指向子类对象。

多态的作用:调用方只管调用,不管细节,当我们新增一种 Animal 的类型时,只要保证 run() 方法编写正确,不用管原来的代码是如何调用的。这符合“开闭”原则(Open Closed Principle,OCP):

对扩展开放(open for extension):允许新增 Animal 类的子类;
对修改关闭( closed for modification):新增了 Animal 类的子类,不需要修改依赖 Animal 类的 run_twice() 等函数。

特别注意的一点

对于静态语言(如 Java)来说,传入run_twice() 方法中的类型,需要是 Animal 类型,或者是 Animal 类型的子类型,否则,无法调用 run_twice() 方法。
而 Python 是一门动态语言,则不一定需要传入的是 Animal 类型或者其子类型,只要保证传入的对象有一个 run() 方法就可以了。

例如,这里我们定义一个火车Train 类,它有一个 run() 方法,它就可以传入 run_twice() 方法。

class Train(object):

    def run(self):
        print('Train is running...')

run_twice(Train())
'''
Train is running...
Train is running...
'''

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

9.4 获取对象信息

这里主要学习的是 type() 函数和 isinstance() 函数的使用。它们都是 Python 的内置函数。

type() 函数

type() 函数用来获取对象的类型,返回的是对应的 Class 类型。

它可以用于基本类型数据:

#  使用 type()来判断基本类型
print(type(123)) # <class 'int'>
print(type('hello')) # <class 'str'>
print(type(None))  # <class 'NoneType'>
print(type(3.1415926)) # <class 'float'>
print(type(True)) # <class 'bool'>

可以判断指向函数或类的变量的类型:

print(type(abs)) # <class 'builtin_function_or_method'>
class Animal(object):
    pass
a = Animal()
print(type(a)) # <class '__main__.Animal'>
b = Animal
print(type(b)) # <class 'type'>

判断 type() 函数作用的变量的类型是否相等:

print(type(123) == type(456)) # True
print(type(123) == int) # True
print(type('abc') == type('123')) # True
print(type('abc') == str) # True
print(type(6.18) == type(3.14)) # True
print(type(6.18) == float) # True
print(type(1 > 0) == type(2 > 1)) # True
print(type(1 > 0) == bool) # True

可以看到,可以让基本数据类型的 intfloat 等参与比较。

判断 type() 函数作用的对象是否是函数:

import types # 引入 types 模块,这里需要它里面定义的一些常量
def fn():
    pass

print(type(fn) == types.FunctionType) # True
print(type(abs) == types.BuiltinMethodType) # True
print(type(lambda x:  x + 1) == types.LambdaType) # True
print(type((x for x in range(10))) == types.GeneratorType) # 这种是生成器列型 打印 :True 

isinstance() 函数

class Fruit(object):
    pass

class Apple(Fruit):
    pass

class Hongfushi(Apple):
    pass

f = Fruit()
a = Apple()
h = Hongfushi()

print(isinstance(f, Fruit)) # True
print(isinstance(a, Fruit)) # True
print(isinstance(h, Fruit)) # True
print(isinstance(a, Hongfushi)) # False

# isinstance 用于基本数据类型
print(isinstance(123, int)) # True
print(isinstance(True, bool)) # True
print(isinstance(3.14, float)) # True
print(isinstance('hello', str)) # True

# isinstance 判断一个变量是否某些类型中的一种
print(isinstance([1, 2, 3], (list, tuple))) # True

print('=' * 20, 'I am a divider', '=' * 20)

知识点:type() 函数和 isinstance() 函数的区别是什么?

type() 不会认为子类是一种父类类型,不考虑继承关系。

isinstance() 会认为子类是一种父类类型,考虑继承关系。

如果要判断两个类型是否相同推荐使用 isinstance()。

dir() 函数

获取一个对象的所有属性和方法,使用 dir() 函数,这也是 Python 中的一个内置函数。

print(dir('ABC'))

打印结果:

['__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__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

可以看到返回的是一个 list,里面包含了str 对象的所有属性和方法。

# 获取一个对象的长度, 使用 len() 函数,内部就是调用对象的 __len__ 函数
print(len('ABC'))
print('ABC'.__len__())

在自己的类中使用 __len__ 函数:

# 自己编写一个类,定义一个 __len__ 方法,也可以使用 len() 函数获取长度
class MyObject(object):
    def __len__(self):
        return 100

print(MyObject().__len__()) # 100
print(len(MyObject())) # 100

配合 getattr(), setattr() 和 hasattr(), 直接操作一个对象的状态

# 配合 getattr(), setattr() 和 hasattr(), 直接操作一个对象的状态
# 这三个都是内置函数
class MyClass(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x * self.x
obj = MyClass()
# 判断有无属性 x?
print(hasattr(obj, 'x')) # True
print(obj.x) # 9
# 判断有无属性 y? 
print(hasattr(obj, 'y')) # False
# 设置一个属性 y?
setattr(obj, 'y', 19)
# 再判断有无属性 y? 
print(hasattr(obj, 'y')) # True
# 获取属性 y
print(getattr(obj, 'y')) # 19
# 获取一个不存在的属性,抛出异常
# getattr(obj, 'z') # 此行抛出异常,如下所示
'''
  File "attrs.py", line 36, in <module>
    getattr(obj, 'z')
AttributeError: 'MyClass' object has no attribute 'z'
'''
# 获取属性,如果不存在,返回默认值
print(getattr(obj, 'z', -1)) # -1

# 获取对象的方法
print(hasattr(obj, 'power')) # True
print(getattr(obj, 'power')) # <bound method MyClass.power of <__main__.MyClass object at 0x7f5d8dd5cf98>>
fn = getattr(obj, 'power')
print(fn) # <bound method MyClass.power of <__main__.MyClass object at 0x7f5d8dd5cf98>>
print(fn()) # 81

9.5 实例属性和类属性

# 实例属性和类属性
class Student(object):
    # name 是类属性,归 Student 类所有
    name = 'Student'

wang = Student()
print(wang.name) # Student

li = Student()
print(li.name) # Student

可以看到,对于 name,我们没有通过 __init__ 函数进行绑定,也没有通过对象绑定,但是每个实例都已经有了这个属性。

类属性作为计数器的例子:

# 为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:
class Student(object):
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count = Student.count + 1

需要注意的是,不要对实例属性和类属性使用相同的名字,因为相同名字的实例属性会屏蔽掉类属性。

实例属性和类属性的区别:

实例属性属于各个实例,互不干扰;

类属性属于类所有,所有实例共享一个属性。

发布了84 篇原创文章 · 获赞 56 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/willway_wang/article/details/105014675