Python GIL/Copy/私有/面向对象
1. GIL
- Python语言和GIL没有关系。仅仅是由于历史原因在Cpython虚拟机(解释器),难以移除GIL。
- GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码。
- 线程释放GIL锁的情况: 在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100
- Python使用多进程是可以利用多核的CPU资源的。
- 多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁
2. 深拷贝和浅拷贝
# 1. 引用 # 2. 浅拷贝 # 浅拷贝是对于一个对象的顶层拷贝 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,[10, 11, 12]] print(id(a)) # 2201716937288 print(id(a[10])) # 2829155127880 b = copy.copy(a) print(b) print(id(b)) # 2201716937800 print(id(b[10])) # 2829155127880 # 只复制了顶层,引用底层,底层改变,浅拷贝得来的值也会跟着改变 # [:]和copy.copy()一样属于浅拷贝 # 若果浅拷贝一个字典,键是拷贝,值是引用 # 3. 深拷贝 # 深拷贝是对于一个对象所有层次的拷贝(递归) # 不只是顶层的id改变,底层的id也改变 # 源数据发生改变,关我何事 # 4. 注意 # copy.copy()对于可变类型,会进行浅拷贝 # copy.copy()对于不可变类型,不会拷贝,仅仅是指向 # 浅拷贝中有引用,那就引用 # 深拷贝中有引用,会递归复制
3. 私有化
- xx: 公有变量
- _x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
- __xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)
- __xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:
__init__
, __ 不要自己发明这样的名字 - xx_:单后置下划线,用于避免与Python关键词的冲突
通过name mangling(名字重整(目的就是以防子类意外重写基类的方法或者属性)如:_Class__object)机制就可以访问private了。
class Person(object): def __init__(self, name, age, taste): self.name = name self._age = age self.__taste = taste def showperson(self): print(self.name) print(self._age) print(self.__taste) def dowork(self): self._work() self.__away() def _work(self): print('my _work') def __away(self): print('my __away') class Student(Person): def construction(self, name, age, taste): self.name = name self._age = age self.__taste = taste def showstudent(self): print(self.name) print(self._age) print(self.__taste) @staticmethod def testbug(): _Bug.showbug() # 模块内可以访问,当from cur_module import *时,不导入 class _Bug(object): @staticmethod def showbug(): print("showbug") s1 = Student('jack', 25, 'football') s1.showperson() print('*'*20) # 无法访问__taste,导致报错 # s1.showstudent() s1.construction('rose', 30, 'basketball') s1.showperson() print('*'*20) s1.showstudent() print('*'*20) Student.testbug()
总结:
- 父类中属性名为
__名字
的,子类不继承,子类不能访问 - 如果在子类中向
__名字
赋值,那么会在子类中定义的一个与父类相同名字的属性 _名
的变量、函数、类在使用from xxx import *
时都不会被导入
4. 封装、继承、多态
为什么要封装
好处:
- 在使用面向过程编程时,当需要对数据处理时,需要考虑用哪个模板中哪个函数来进行操作,但是当用面向对象编程时,因为已经将数据存储到了这个独立的空间中,这个独立的空间(即对象)中通过一个特殊的变量(__class__)能够获取到类(模板),而且这个类中的方法是有一定数量的,与此类无关的将不会出现在本类中,因此需要对数据处理时,可以很快速的定位到需要的方法是谁 这样更方便
- 全局变量是只能有1份的,多很多个函数需要多个备份时,往往需要利用其它的变量来进行储存;而通过封装 会将用来存储数据的这个变量 变为了对象中的一个“全局”变量,只要对象不一样那么这个变量就可以再有1份,所以这样更方便
- 代码划分更清晰
为什么要继承
- 能够提升代码的重用率,即开发一个类,可以在多个子功能中直接使用
- 继承能够有效的进行代码的管理,当某个类有问题只要修改这个类就行,而其继承这个类的子类往往不需要就修改
怎样理解多态
Python中多态的作用
让具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容(功能)的函数。
Python中多态的特点
1、只关心对象的实例方法是否同名,不关心对象所属的类型;
2、对象所属的类之间,继承关系可有可无;
3、多态的好处可以增加代码的外部调用灵活度,让代码更加通用,兼容性比较强;
4、多态是调用方法的技巧,不会影响到类的内部设计。
多态的应用场景
1. 对象所属的类之间没有继承关系
调用同一个函数fly()
, 传入不同的参数(对象),可以达成不同的功能
class Duck(object): # 鸭子类 def fly(self): print("鸭子沿着地面飞起来了") class Swan(object): # 天鹅类 def fly(self): print("天鹅在空中翱翔") class Plane(object): # 飞机类 def fly(self): print("飞机隆隆地起飞了") def fly(obj): # 实现飞的功能函数 obj.fly() duck = Duck() fly(duck) swan = Swan() fly(swan) plane = Plane() fly(plane) # 运行结果: 鸭子沿着地面飞起来了 天鹅在空中翱翔 飞机隆隆地起飞了
2. 对象所属的类之间有继承关系(应用更广)
class gradapa(object): def __init__(self,money): self.money = money def p(self): print("this is gradapa") class father(gradapa): def __init__(self,money,job): super().__init__(money) self.job = job def p(self): print("this is father,我重写了父类的方法") class mother(gradapa): def __init__(self, money, job): super().__init__(money) self.job = job def p(self): print("this is mother,我重写了父类的方法") return 100 #定义一个函数,函数调用类中的p()方法 def fc(obj): obj.p() gradapa1 = gradapa(3000) father1 = father(2000,"工人") mother1 = mother(1000,"老师") fc(gradapa1) #这里的多态性体现是向同一个函数,传递不同参数后,可以实现不同功能. fc(father1) print(fc(mother1)) # 运行结果: this is gradapa this is father,我重写了父类的方法 this is mother,我重写了父类的方法 100