Python之旅下

Posted by Wh0ami-hy on April 25, 2022

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) #直接访问私有属性

  • 定义方法访问

可以通过定义getset 方法来实现访问私有

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


本站总访问量