自己以前整理的笔记,不太完整,后续会不断更新。。。。
一、GIL全局解释器锁
单进程单线程程序开多个、单进程多线程、多进程中多线程是假的,因为python解释器(Cpython)中存在GIL,每个线程在执行的过程都需要先获取GIL,它保证同一时间CPU只能执行一个线程。
而单进程单线程程序开多个、多进程可最大化使用多核CPU性能。
只有Cpython解释器中存在GIL
多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁
计算密集型:充分调用CPU的计算性能,大量运算------>进程
IO密集型:读写操作频繁,如:网络传输,文件的读写--------->线程/协程
解决GIL问题的方法:
- 更换python解释器
- 在子线程中引入其他编程语言
二、 深、浅拷贝
import copy
深拷贝:copy.deepcopy()
浅拷贝:copy.copy()
- 浅拷贝是对于一个对象的顶层拷贝
- 深拷贝是对于一个对象的深层拷贝,所有数据均进行拷贝
- 如果浅拷贝拷贝的是元组,它不会进行浅拷贝,仅仅是指向元组
- 如果用copy.copy()、copy.deepcopy()对一个全部都是不可变类型的数据进行拷贝,那么他们都是拷贝引用
- 如果拷贝的是一个不可变类型的数据,内部包含可变类型数据,那么copy.deepcopy()是深层拷贝,而copy.copy()还是拷贝引用
切片拷贝
切片是浅拷贝
字典拷贝
字典中的value值都是引用,那么当浅拷贝时拷贝的是引用,深拷贝依然是全复制数据
import copy
def main():
dic1=dict(name='zhangshan',age=18,temp=[1,2])
co1=copy.copy(dic1) # 浅拷贝,拷贝的引用
dic1['temp'].append(3)
print(id(dic1))
print(id(co1))
print(dic1)
print(co1)
co2=copy.deepcopy(dic1) # 深拷贝,拷贝整个数据
dic1['temp'].append(3)
print(id(dic1))
print(id(co2))
print(dic1)
print(co2)
if __name__ == '__main__':
main()
深拷贝的应用:
在获取源数据后,要进行操作,为不污染源数据,可采用深拷贝而不影响源数据
三、 import
变量类型:
_xx:单前置下划线变量,当作为模块导入其他程序中时,该变量不可访问
__xx:双前置下划线变量,私有属性或方法,子类无法继承和使用
__xx__:前后双下划线,魔法方法或属性
xx_:单后置下划线,用于避免与python关键词冲突
import aa as AA:则以aa作为模块名无法使用
注意变量名不要与模块名重名
模块导入路径:
可在sys.path中查看,返回一个搜索路径列表,最先搜索当前路径,再搜索sys.path中的其他路径
若没有要使用模块的路径,则可以在sys.path列表中添加
sys.path.append('/home/itcast/xxx')
sys.path.insert(0, '/home/itcast/xxx')
在程序中import xx如果出现多次,则只有第一次的导入有效
程序中导入某一模块后,一直运行,若修改模块中的内容,那么不会影响到运行程序中的模块
这时可以使用
import xx
....
from imp import reload
reload(xx)
多模块开发注意事项
多个模块共享数据时的坑 ????????????????
from module import data
从其他模块导入的数据data,相当于在本程序内创建了一个全局变量data,其值与模块变量值相同。但如果通过赋值的方式修改该数据则只能修改本地的全局变量,被导入模块中的数据不会变。
要同步改变被导入模块的数据,可以采用以下方法:
- 通过数据内置的方法,如列表的append\extend等
- 通过通过module.data的方式调用数据
四、super()调用顺序
多重继承嵌套多继承的情况下,super()保证所有父类只调用一次
super().__init__相对于 类名.__init__,在单继承上用法基本无差,但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次
使用类名.__mro__可以查看当前类对父类的继承顺序,返回值是一个类排序元组
更多见:super()对多继承的影响
五、实例方法、静态方法和类方法
方法包括:实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同
- 实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;
- 静态方法:由类调用;无默认参数;
六、property属性
一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法
- 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
- 调用时,无需括号
- 一般要有返回值
创建property属性的两种方法:
- 装饰器 即在方法上应用装饰器
- 类属性 即在类中定义值为property对象的类属性
1.装饰器方式
在类的实例方法上应用@property装饰器
Python中的类有经典类
和新式类
,新式类
的属性比经典类
的属性丰富
经典类,具有一种@property装饰器
@property
def price(self):
return "laowang"
新式类,具有三种@property装饰器
由于新式类中具有三种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
# *-* coding:utf-8 *-*
# 三种装饰器,只能在新式类中使用
# 经典类中只能使用@property
class Goods:
def __init__(self, price):
self.original_price = price
self.discount = 0.8
@property
def price(self):
new_price = self.original_price * self.discount
return new_price
@price.setter # 方法名必须与@property下的方法名相同
def p1(self, value): # 这个方法名可以不同,但是在实例访问时要对应
self.original_price = value
@price.deleter # 方法名必须与@property下的方法名相同
def p2(self):
del self.original_price
apple = Goods(10)
print(apple.price)
apple.p1 = 20 # 执行该语句后调用p1方法
print(apple.price)
del apple.p2 # 执行该语句后调用p2方法
# print(apple.price) # 删除对象属性后报错
2.类属性方式
当使用类属性的方式创建property属性时,经典类
和新式类
无区别
class Foo(object):
def get_bar(self):
print("getter...")
return 'laowang'
def set_bar(self, value):
"""必须两个参数"""
print("setter...")
return 'set value' + value
def del_bar(self):
print("deleter...")
return 'laowang'
BAR = property(get_bar, set_bar, del_bar, "description...")
obj = Foo()
obj.BAR # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__ # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法
property方法中有个四个参数
- 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
- 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
- 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
- 第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息
property的应用:
使用公有方法实现对私有属性的访问和设置
七、魔法属性及方法
__doc__属性
表示类的描述信息
class Foo:
""" 描述类信息,这是用于看片的神奇 """
def func(self):
pass
print(Foo.__doc__)
__module__和__class__属性
- __module__ 表示当前操作的对象在哪个模块
- __class__ 表示当前操作的对象的类是什么,如果是其他模块的类也会显示模块名
__call__方法
对象后面加括号,触发执行
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
__dict__属性
类或对象中的所有属性,以字典的格式输出
__getitem__、__setitem__、__delitem__方法
用于索引操作,如字典。以上分别表示获取、设置、删除数据
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj['k1'] # 自动触发执行 __getitem__
obj['k2'] = 'laowang' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__
__getslice__、__setslice__、__delslice__方法
该三个方法用于分片操作,如:列表
class Foo(object):
def __getslice__(self, i, j):
print('__getslice__', i, j)
def __setslice__(self, i, j, sequence):
print('__setslice__', i, j)
def __delslice__(self, i, j):
print('__delslice__', i, j)
obj = Foo()
obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__
八、with与上下文管理器
with语句
对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源
Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,因为系统允许你打开的最大文件数量是有限的
对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections",因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。
普通打开文件方式:
def m1():
f = open("output.txt", "w")
f.write("python之禅")
f.close()
这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放
进阶打开文件方式:
def m2():
f = open("output.txt", "w")
try:
f.write("python之禅")
except IOError:
print("oops error")
except Exception as e:
pass
finally:
f.close()
将close放在finally中,这样无论是否发生异常,都会执行该语句
高级打开文件方式:
def m3():
with open("output.txt", "r") as f:
f.write("Python之禅")
open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法,无论读写文件是否成功
但如果open语句发生错误,则直接退出with语句
上下文管理器
任何实现了 __enter__() 和 __exit__() 方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器
原理:紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。
class Sample:
def __enter__(self):
print("In __enter__()")
return "Foo"
def __exit__(self, type, value, trace):
print("In __exit__()")
def get_sample():
return Sample()
with get_sample() as sample:
print("sample:", sample)
# In __enter__()
# sample: Foo
# In __exit__()
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
print("entering")
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, *args):
print("will exit")
self.f.close()
with File('out.txt', 'w') as f:
print("writing")
f.write('hello, python')
__enter__() 方法返回资源对象,赋值给f
__exit__() 方法处理一些清除工作,当with下的处理语句发生异常或执行完后自动执行__exit__() 方法
自定义上下文管理器
# 自定义上下文管理器:DummyResource
# 注意with代码的执行顺序:
# 1 执行DummyResource对象--对象的初始化,执行__enter__()方法,(可以用as接收__enter__的返回值)
# 2 执行with下的语句
# 3 执行DummyResource中的__exit__()方法
class DummyResource:
def __init__(self, tag):
self.tag = tag
print('Resource [%s]' % tag)
def __enter__(self):
print('[Enter %s]: Allocate resource.' % self.tag)
return self # 可以返回不同的对象
def __exit__(self, exc_type, exc_value, exc_tb): # 三个参数用于对异常的判断
print('[Exit %s]: Free resource.' % self.tag)
if exc_tb is None:
print('[Exit %s]: Exited without exception.' % self.tag)
else:
print('[Exit %s]: Exited with exception raised.' % self.tag)
with DummyResource('Normal'):
print('[with-body] Run without exceptions.')
另外一种实现方式
Python 还提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 __enter__ 方法中执行,yield 之后的语句在 __exit__ 方法中执行。紧跟在 yield 后面的值是函数的返回值
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
九 、元类
python中类同样也是一种对象,也能
- 你可以将它赋值给一个变量
- 你可以拷贝它
- 你可以为它增加属性
- 你可以将它作为函数参数进行传递
使用type创建类
type除了能判断数据类型,还有一种完全不同的功能,动态的创建类
type可以接受一个类的描述作为参数,然后返回一个类
type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
def echo_bar(self): # 定义了一个普通的函数
print(self.bar)
@staticmethod
def test_static():
print("static method ....")
Test2 = type("Test2", (Test1,), {'bar': True,'echo_bar':echo_bar,'test_static':test_static}) # 定了一个Test2类
# 添加的属性是类属性,并不是实例属性
# type的第2个参数,元组中是父类的名字,而不是字符串
# 添加一个实例方法echo_bar
# 添加一个静态方法test_static
在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的
元类就是用来创建这些类(对象)的,元类就是类的类
函数type实际上是一个元类,type就是Python在背后用来创建所有类的元类
Python中所有的东西,它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type
type就是Python的内建元类,当然了,你也可以创建自己的元类
自定义元类
元类的主要目的就是为了当创建类时能够自动地改变类
元类的创建可以通过继承type元类来实现
当然也可以自定义:
python2中在定义一个类的时候为其添加__metaclass__属性
class Foo(object):
__metaclass__ = something…
Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,继续在父类中寻找,如果任何父类中都没又找到,就会用内建的type来创建这个类
对应的在 python3中使用的是metaclass=元类名
class Foo(object, metaclass=元类名):
bar = 'bip'
上述语句的意思就是,通过xxx元类来创建类对象Foo
元类可以接收当前类的类名,父类信息,属性信息,其形式与type()创建类时的参数形式类似
当出现class Foo()时,即说明创建了元类xxx的“实例对象”,此时,可以通过重写元类中的__new__方法来实现创建类Foo的自定义,同时也可接收上述的Foo类的相关信息
十、ORM
ORM 是 python编程语言后端web框架 Django的核心思想,“Object Relational Mapping”,即对象-关系映射,简称ORM
要实现的功能是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句