1. 类
1.1. 类和实例
- 类是具有相同属性和方法的一组对象的集合,实例是一个个具体的对象。
- 方法是与实例绑定的函数。
- 获取对象信息可使用下面方法:
type(obj)
:来获取对象的相应类型;isinstance(obj, type)
:判断对象是否为指定的 type 类型的实例;hasattr(obj, attr)
:判断对象是否具有指定属性/方法;getattr(obj, attr[, default])
获取属性/方法的值, 要是没有对应的属性则返回 default 值(前提是设置了 default),否则会抛出 AttributeError 异常;setattr(obj, attr, value)
:设定该属性/方法的值,类似于 obj.attr=value;dir(obj)
:可以获取相应对象的所有属性和方法名的列表:
1.2. 继承和多态
- 继承可以拿到父类的所有数据和方法,子类可以重写父类的方法,也可以新增自己特有的方法。
- 有了继承,才有了多态,不同类的对象对同一消息会作出不同的相应
1.3. 类方法和静态方法
- 类方法使用
@classmethod
装饰器,可以使用类(也可使用实例)来调用方法。 - 静态方法使用
@staticmethod
装饰器,它是跟类有关系但在运行时又不需要实例和类参与的方法,可以使用类和实例来调用。
1.4. 定制类和魔法方法
__new__
在__init__
之前被调用,用来创建实例。__str__
是用 print 和 str 显示的结果,__repr__
是直接显示的结果。__getitem__
用类似obj[key]
的方式对对象进行取值__getattr__
用于获取不存在的属性 obj.attr__call__
使得可以对实例进行调用1.5. slots魔法
- slots 魔法:限定允许绑定的属性.
__slots__
设置的属性仅对当前类有效,对继承的子类不起效,除非子类也定义了 slots,这样,子类允许定义的属性就是自身的 slots 加上父类的 slots。
class Person(object):
__slots__ = ('name', 'age') #限制了动态属性
def __init__(self, name, age):
self.name = name
self.age = age
p = Person('张三', 18)
p.name = '李李四'
p.height = 180 #报错
对象p只能设置name和age属性,不能再动态添加属性 p.height = 180
1.6. 使用@property
@property
把方法『变成』了属性。
1.7. super
- 事实上,
super
和父类没有实质性的关联。 super(cls, inst)
获得的是 cls 在 inst 的 MRO 列表中的下一个类。
1.8. metaclass
Python 中的元类(metaclass)是一个深度魔法,平时我们可能比较少接触到元类
- 在 Python 中,类也是一个对象。
- 类创建实例,元类创建类。
- 元类主要做了三件事:
- 拦截类的创建
- 修改类的定义
- 返回修改后的类
- 当你创建类时,解释器会调用元类来生成它,定义一个继承自 object 的普通类意味着调用 type 来创建它。
2. python面向对象编程
2.1. 定义类
方法里的第一个参数必须是self
class 类名: def 方法1(self,参数列表): pass def 方法2(self,参数列表): pass
2.2. 创建实例对象
对象变量名 = 类名() #如 a = A()
class People: def init(self,x,y,z): self.name = x self.age = y self.height = z def run(self): print(“正在跑步”)
def eat(self):
print("正在吃东西")
def __str__(self):
return '自动调用print'
s1 = People(‘小明’,16,180) #这段代码做了 #调用__new__方法用来申请内存空间 #调用__init__方法,并让self指向申请好的那段内存空间,填充数据 #让s1也指向那段内存空间
print(s1) #结果:自动调用print
2.3. self的使用
- 给对象添加属性
class People: def init(self,x,y,z): self.name = x #添加属性 self.age = y self.height = z
- python支持动态属性,当一个对象创建好了以后,直接使用
对象.属性名 = 属性值
就可以很方便的给对象添加一个属性。但是,不建议使用这种方式给对象添加属性
tom = Cat() tom.name = ‘Tom’ # 可以直接给 tom 对象添加⼀一个 name 属性
2.3.1. self概念
哪个对象调用了方法,方法里的self
指的就是谁。通过self.属性名
可以访问到这个对象的属性。通过self.方法名()
可以调用这个对象的方法
2.4. 魔术方法
- 特点
两个下划线开始,两个下划线结束的方法
魔术方法在恰当的时候就会被激活,自动执行
__init__
方法
该方法在创建对象时,会默认被调用
方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好的对象引 用直接赋值给self
在类的内部,可以使用self来使用属性和调用方法。在类的外部,需要使用对象名来使用属性和调用方法
如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址
方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个对象调用了实例方法
__del__
方法
该方法在删除对象时,会默认被调用
__str__
方法
返回对象的描述信息,使用print()
函数打印对象时,其实调用的就是这个对象的 __str__
方法
如果想要修改对象的输出的结果,可以重写__str__
方法
__repr__
方法
__repr__
方法和 __str__
方法功能类似,都是用来修改一个对象的默认打印内容。
在打印一个对象时,如果没有重写__str__
方法,它会自动来查找__repr__
方法。
如果这两个方法都没有,会直接打印这个对象的内存地址。
如果这两个方法都有,会调用__str__
方法
__call__
对象后面加括号,触发执⾏
class People: def init(self,x,y,z): self.name = x self.age = y self.height = z def run(self): print(“正在跑步”)
def eat(self):
print("正在吃东西")
def __call__(self):
print("__call方法被调用")
s1 = People(‘小明’,16,180)
s1() #调用__call__方法
class People: def init(self,x,y,z): self.name = x self.age = y self.height = z def run(self): print(“正在跑步”)
def eat(self):
print("正在吃东西")
def __call__(self,*args,**kwargs):
print("__call方法被调用")
print("{}\n".format(args))
print("{}\n".format(kwargs))
fun = kwargs['fn']
print(fun(args[0], args[1]))
s1 = People(‘小明’,16,180)
s1(1,2,fn = lambda x,y:x+y)
__eq__
==
默认调用对象的__eq__
方法,获取该方法的返回值。若__eq__
方法不重写,则默认比较内存地址
身份运算符is
用来判断两个对象是否是同一个对象
__doc__
表示类的描述信息,获取类中的文档说明'''中的内容'''
,可以类名.__doc__
也可以__对象名.__doc__
调用
class Person: ‘'’一些介绍’’’ def init(self, name, age): self.name = name self.age = age
p = Person(‘张三’, 18)
print(p.doc) #结果:一些介绍
__module__
表示当前操作的对象在那个模块
class Person: ‘'’一些介绍’’’ def init(self, name, age): self.name = name self.age = age
p = Person(‘张三’, 18)
print(p.module) #结果是 main
__class__
表示当前操作的对象的类是什么
class Person: ‘'’一些介绍’’’ def init(self, name, age): self.name = name self.age = age
p = Person(‘张三’, 18)
print(p.class) #结果是 <class ‘main.Person’>
__dict__
以字典的形式,显示对象所有的属性和方法
class Person: ‘'’一些介绍’’’ def init(self, name, age): self.name = name self.age = age
p = Person(‘张三’, 18)
print(p.dict) #结果是 {‘name’: ‘张三’, ‘age’: 18}
2.5. 类属性和对象属性
- 类属性可以通过类对象或者实例对象访问
class Person: number = ‘06’ #类属性 def init(self, name, age): self.name = name self.age = age
p = Person(‘张三’, 18)
print(p.number) #通过实例对象访问类属性 print(Person.number) #通过类对象访问类属性
- 如果有同名实例属性和类属性,实例对象会优先访问实例属性
class Person: number = ‘06’ #类属性 def init(self, name, age,number): self.name = name self.age = age self.number = number #同名属性
p = Person(‘张三’, 18,’08’)
print(p.number) #结果为08,实例对象会优先访问实例属性 print(Person.number) ##结果为06
- 类属性只能通过类对象修改,不能通过实例对象修改
class Person: number = ‘06’ def init(self, name, age): self.name = name self.age = age
p = Person(‘张三’, 18)
p.number = ‘01’ print(Person.number) #结果为06 Person.number = ‘01’ print(Person.number) #结果为01
- 类属性也可以设置为私有,前边添加两个下划线
2.6. 私有属性和方法
对象的属性或者方法只可以在对象的内部使用,在外部不能被访问
2.6.1. 定义
在定义属性或方法时,在属性名或者方法名前增加两个下划线 __
,定义的就是私有属性或方法
2.6.2. 访问私有
- 直接访问
在私有属性名或方法名前添加_类名
class Person: def init(self, name, age): self.__money = 800 #私有属性 self.name = name self.age = age
p = Person(‘张三’, 18) print(p._Person__money) #直接访问私有属性
- 定义方法访问
可以通过定义get
和 set
方法来实现访问私有
class Person: def init(self, name, age): self.__money = 800 self.name = name self.age = age
def get(self):
return self.__money
def set(self,money):
self.__money = money p = Person('张三', 18) print(p.get()) #结果是 800 p.set(2) print(p.get()) #结果是 2
2.7. 类方法
- 对象方法可以使用
实例对象名.方法(参数)
调用,该方法不需要为self
传参,会自动为self
传参
class Person: def init(self, name, age): self.name = name self.age = age def demo(self,number): #类方法 print(self.name + ‘学号是’ + number)
p = Person(‘张三’, 18) p.demo(‘01’) #通过对象调用
#结果 张三学号是01
- 对象方法还可以使用
类名.方法(参数)
调用,该方法不会自动为self
传参,需要手动指定self
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def demo(self,number):
print(self.name + '学号是' + number)
p = Person(‘张三’, 18) Person.demo(p,’02’) #通过类调用 #结果 张三学号是02
2.8. 继承
-
在Python中,继承可以分为单继承、多继承和多层继承
-
定义类时,在类名的括号后面传入父类的类名,表示子类继承父类
class Animal: def init(self): pass ‘'’动物类’’’
def sleep(self):
print('正在睡觉')
class Dog(Animal): #继承于Animal类 ‘'’Dog类继承于Animal类’’’ def init(self): pass
dog = Dog() dog.sleep()
3. 高级特性
3.1. 迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不能后退
迭代对象(Iterable)
含有 __iter__()
方法或 __getitem__()
方法的对象称之为可迭代对象
使用 Python 内置的 hasattr()
函数来判断一个对象是不是可迭代的,也可以使用 isinstance()
进行判断
hasattr([], '__iter__')
isinstance([], Iterable)
迭代器(Iterator)
一个实现了__iter__
方法和__next__
方法的对象,就是迭代器,其中,__iter()__
方法返回迭代器对象本身,next()
方法返回容器的下一个元素,在没有后续元素时抛出 StopIteration
异常
hasattr([1, 2, 3], '__iter__')
isinstance([], Iterator)
虽然元组、列表和字典等对象是可迭代的,但它们却不是迭代器!对于这些可迭代对象,可以使用 Python 内置的 iter()
函数获得它们的迭代器对象
Python 的 for
循环实质上是先通过内置函数 iter()
获得一个迭代器,然后再不断调用 next()
函数实现的
3.2. 生成器
生成器(generator)也是一种迭代器,在每次迭代时返回一个值,直到抛出 StopIteration
异常。它有两种构造方式:
- 生成器表达式
- 生成器函数
生成器表达式
和列表推导式的定义类似,生成器表达式使用 ()
而不是 []
numbers = (x for x in range(5)) # 注意是(),而不是[]
for num in numbers:
print num
生成器函数
含有 yield
关键字的函数,调用该函数时会返回一个生成器
- yield 把函数变成了一个生成器。
- 生成器函数的执行过程看起来就是不断地
执行->中断->执行->中断
的过程。- 一开始,调用生成器函数的时候,函数不会立即执行,而是返回一个生成器对象;
- 然后,当我们使用
next()
作用于它的时候,它开始执行,遇到yield
语句的时候,执行被中断,并返回当前的迭代值,要注意的是,此刻会记住中断的位置和所有的数据,也就是执行时的上下文环境被保留起来; - 当再次使用
next()
的时候,从原来中断的地方继续执行,直至遇到yield
,如果没有yield
,则抛出异常。
3.3. 上下文管理器
- 上下文管理器是支持上下文管理协议的对象,也就是实现了
__enter__
和__exit__
方法。 - 通常,我们使用
with
语句调用上下文管理器。with 语句尤其适用于对资源进行访问的场景,确保执行过程中出现异常情况时也可以对资源进行回收,比如自动关闭文件等。 __enter__
方法在 with 语句体执行前调用,with 语句将该方法的返回值赋给 as 字句中的变量,如果有 as 字句的话。__exit__
方法在退出运行时上下文
时被调用,它负责执行『清理』工作,比如关闭文件,释放资源等。如果退出时没有发生异常,则__exit__
的三个参数,即 type, value 和 traceback 都为 None。如果发生异常,返回 True 表示不处理异常,否则会在退出该方法后重新抛出异常以由 with 语句之外的代码逻辑进行处理
4. 模块(module)
-
python里,一个
.py
文件就是一个模块 -
.py
文件的命名必须遵守规则:由字母、数字、下划线构成,不能以数字开头
4.1. 导入模块
import 模块名
直接导入一个模块
from 模块名 import 函数名
导入一个模块里的方法或变量
from 模块名 import *
导入这个模块的所有方法和变量(不是所有),使用该方法导入一个模块里所有的内容时,本质上是去查找这个模块的__all__
属性,将__all__
属性里声明的所有内容导入。如果这个模块里没有设置__all__
属性,此时才会导入这个模块里的所有内容
import 模块名 as 别名
导入一个模块并为其起别名
from 模块名 import 函数名 as 别名
4.2. pip
- pip install 模块名
下载第三方模块
- pip uninstall 模块名
卸载第三方模块
- pip freeze
列出当前环境安装模块名和版本号
- pip freeze > filename
将安装的模块名和版本号重定向输出到指定的文件
- pip install -r filename
读取文件中的模块名和版本号并安装。该方法常用于将代码部署到服务器时,给服务器的python安装模块
- pip install 模块名 -i 源的地址
(临时的)更改模块的下载源
- 永久更改源地址
在当前用户目录下(C:\Users\用户名
)创建一个pip的文件夹,然后再在文件夹里创建pip.ini文件并输入以下内容
[global] index-url=https://pypi.douban.com/simple [install] trusted-host=pypi.douban.com
#国内镜像 #阿里云 https://mirrors.aliyun.com/pypi/simple/ #中国科技⼤大学 https://pypi.mirrors.ustc.edu.cn/simple/ #豆瓣(douban) https://pypi.douban.com/simple/ #清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/ #中国科学技术大学 https://pypi.mirrors.ustc.edu.cn/simple/
4.3. 模块里的私有成员
模块里以一个下划线 _
开始的变量和函数,是模块里的私有成员,当模块被导入时,以 _
开头的变量默认不会被导入。但是它不具有强制性,如果一个代码强行使用以 _
开头的变量,有时也可以。但是强烈不建议这样使用,因为有可能会出问题
4.4. __all__
一个存放模块变量和方法的列表
4.5. __name__
Python中,当直接运行一个py文件时,这个py文件里的__name__
值是 __main__
,当这个py文件被当作模块导入时__name__
的值是文件名 ,据此可以判断一个py文件是被直接执行还是以模块的形式被导入。
if name == ‘main’: print ‘This program is being run by itself’ else: print ‘I am being imported from another module’
5. 包
一般把 Python 里的一个代码文件夹称为一个包,并在包里创建__init__.py
文件
导包
以项目根目录为准,输入目标模块 或 包的路径: from spiders._tools import xxx