在 Python 中,一个 .py 文件就称之为一个模块(Module)。
模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块所使用。
模块命名不要和系统模块名冲突,在 Python 交互式环境中,import abc
,若成功说明存在此名称的系统模块;否则,不存在。
sys
模块是 Python 内置的一个模块。
使用 sys
模块的第一步,就是导入该模块:
import sys
导入 sys
模块后,就有了 sys
变量指向该模块,这样通过 sys
变量,就可以访问 sys
模块的所有功能。
作用域
正常的函数和变量名是公开的(public),可以被直接引用,比如 abc
, PI
等;
类似__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊的用途,比如 __author__
,__name__
就是特殊变量,自己一般不要这样命名;
类似_xxx
和 __xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,如 _abc
, __abc
等。
_xxx
这样的函数或变量是可以被外界访问到的。但是,按照约定俗成的规定,这样的函数或变量表示的含义是“虽然我可以被访问,但是请把我视为私有变量,不要随意访问”。
在 Python 中,安装第三方模块,是通过包管理工具 pip 完成的。
第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索。
比如安装 Pillow,命令是:
pip install Pillow
安装常用模块
使用Anaconda,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库。Anaconda 自带 Python。
在 Python 中,所有数据类型都可以视为对象,也可以自定义对象。
在 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) #
从打印信息可以看出,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__
方法,在创建实例的时候把 name
,score
等属性绑定上去。
__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
传进去。因此,需要传入的是 name
和 score
。
普通函数和类中定义的函数有什么区别?
区别只有一个,就是在类中定义的函数,它的第一个参数永远是实例变量 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
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
print(wang.height)
AttributeError: 'Student' object has no attribute 'height'
'''
隐藏变量,对外暴露 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
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
继承
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...
'''
这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
这里主要学习的是 type()
函数和 isinstance()
函数的使用。它们都是 Python 的内置函数。
type() 函数
type()
函数用来获取对象的类型,返回的是对应的 Class 类型。
它可以用于基本类型数据:
# 使用 type()来判断基本类型
print(type(123)) #
print(type('hello')) #
print(type(None)) #
print(type(3.1415926)) #
print(type(True)) #
可以判断指向函数或类的变量的类型:
print(type(abs)) #
class Animal(object):
pass
a = Animal()
print(type(a)) #
b = Animal
print(type(b)) #
判断 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
可以看到,可以让基本数据类型的 int
、float
等参与比较。
判断 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
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')) # >
fn = getattr(obj, 'power')
print(fn) # >
print(fn()) # 81
# 实例属性和类属性
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
需要注意的是,不要对实例属性和类属性使用相同的名字,因为相同名字的实例属性会屏蔽掉类属性。
实例属性和类属性的区别:
实例属性属于各个实例,互不干扰;
类属性属于类所有,所有实例共享一个属性。