首页 文章 精选 留言 我的

精选列表

搜索[数据库知识开放麦],共10000篇文章
优秀的个人博客,低调大师

Python高级知识点学习(六)

围棋少年 Python中的迭代协议 迭代协议有两个概念: 可迭代类型(Iterable) 迭代器(Iterator) 迭代器:迭代器是访问集合内元素的一种方式, 一般用来遍历数据。 迭代器和以下标的访问方式不一样, 迭代器是不能返回的, 迭代器提供了一种惰性方式数据的方式。 可迭代对象(Iterable) 和 迭代器(Iterator) 是不同的。 可迭代对象: 实现__iter__这个魔法函数 迭代器: 实现__next__这个魔法函数 实现__iter__这个魔法函数 from collections.abc import Iterable, Iterator a = [1, 2] print(isinstance(a, Iterable)) print(isinstance(a, Iterator)) 打印结果: True False 上边代码,因为a是一个list,而list是一个可迭代对象并不是迭代器,因为list对象中没有__next__方法。 生成器 生成器函数:函数里只要有yield关键字,它就是生成器对象。 生成器对象在python编译字节码的时候就产生了。 生成器对象也是实现了迭代器协议的,所以可以使用for循环遍历到它的值。 def gen_func(): yield 1 gen = gen_func() for value in gen: print(value) 打印结果: 1 Python 中的GIL GIL:global interpreter lock (cpython) GIL:全局解释器锁。 python中一个线程对应于c语言中的一个线程。 GIL使得同一个时刻只有一个线程在一个cpu上执行字节码, 也就意味着无法将多个线程映射到多个cpu上执行。 GIL锁分配给某一线程后,并不是说这个线程执行完了之后它才会释放把它交给另外一个线程,它不是整个过程完全占有,它实际上是会在适当的时刻释放的,是结合了字节码执行的行数比如他执行了1000行字节码之后,它会释放,然后另外一个线程就可以得到运行。 GIL释放: 会根据执行的字节码行数以及时间片释放gil。 gil在遇到io的操作时候主动释放。 total = 0 def add(): global total for i in range(1000000): total += 1 def desc(): global total for i in range(1000000): total -= 1 thread1 = threading.Thread(target=add) thread2 = threading.Thread(target=desc) thread1.start() thread2.start() thread1.join() thread2.join() print(total) 上边代码两个线程分别执行两个函数,两个函数对同一变量做加减操作,本来应该先加到1000000再减1000000最终打印出0,但事实上是不会打印0的。 多线程编程 IO密集型时,适合多线程。 CPU密集型时,适合多进程。 多线程编程是我们几乎所有编程语言中都会遇到的问题。 操作系统能够切换和调度的最小单元是线程。 在最开始的时候,操作系统能够调度的最小单元是进程,但是由于进程对系统资源消耗非常大,所以后期就演变出了线程。 第一种方式:通过Thread类实例化 def get_detail_html(url): print("get detail html started") time.sleep(2) print("get detail html end") def get_detail_url(url): print("get detail url started") time.sleep(4) print("get detail url end") if __name__ == "__main__": thread1 = threading.Thread(target=get_detail_html, args=("",)) thread2 = threading.Thread(target=get_detail_url, args=("",)) # 设置为守护线程,随着主线程退出,子线程也退出 #thread1.setDaemon(True) #thread2.setDaemon(True) start_time = time.time() thread1.start() thread2.start() # 等待线程1,2执行完成 再执行完主线程; thread1.join() thread2.join() print(time.time() - start_time) 上边代码中,两个线程分别执行两个函数,主线程下有两个子线程。 thread1.setDaemon(True)这个操作是把thread1设置为守护线程,随着主线程退出,thread1也退出。thread1.join()这个操作是把主线程等待thread1执行完再执行完主线程。 第二种方式:通过重载Thread来实现多线程 def get_detail_html(url): print("get detail html started") time.sleep(2) print("get detail html end") def get_detail_url(url): print("get detail url started") time.sleep(4) print("get detail url end") class GetDetailHtml(threading.Thread): def __init__(self, name): super().__init__(name=name) def run(self): print("get detail html started") time.sleep(2) print("get detail html end") class GetDetailUrl(threading.Thread): def __init__(self, name): super().__init__(name=name) def run(self): print("get detail url started") time.sleep(4) print("get detail url end") if __name__ == "__main__": thread1 = GetDetailHtml("get_detail_html") thread2 = GetDetailUrl("get_detail_url") start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() print("last time: {}".format(time.time()-start_time)) 上边代码继承threading.Thread必须重载run方法。 线程同步Lock、RLock 为什么要线程同步? 现有两个函数,分别是对全局变脸a进行加减操作,两个函数使用两个线程来运行,一个线程负责把a加一,另一个负责把a减一,上代码: a = 0 def add(a): a += 1 def desc(a): a-=1 首先使用内置方法dis()看一下两个函数字节码是什么样子的: import dis def add(a): a += 1 def desc(a): a-=1 print(dis.dis(add)) print(dis.dis(desc)) 打印结果: 63 0 LOAD_FAST 0 (a) 2 LOAD_CONST 1 (1) 4 INPLACE_ADD 6 STORE_FAST 0 (a) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE None 66 0 LOAD_FAST 0 (a) 2 LOAD_CONST 1 (1) 4 INPLACE_SUBTRACT 6 STORE_FAST 0 (a) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE None 上边代码打印结果: 看下add的里边字节码: LOAD_FAST:首先把 a LOAD 到内存中 LOAD_CONST :再把1 LOAD 到内存中 INPLACE_ADD:执行加的操作 STORE_FAST:将加完的值赋值给 a desc里边也是一样的,分四步,不同的是desc里执行的是减法。 如果同时执行add字节码和desc字节码时,之前笔记中提过,执行以上四个步骤时随时都可能释放gil锁,因为字节码的数量已经满了,以上四步任何一步骤都可能释放gil锁切换到另外一个线程,所以有可能造成一个结果,就是a要么等于1,要么等于-1,但是我们期望的是a 等于0,这时候就需要线程同步来解决问题。 我们希望在执行add函数的代码段时,另一个线程中的desc代码段是停止的,这就是线程同步机制。 python给我们提供了一个机制,叫做锁:from threading import Lock 在运行一个代码段时,加一把锁,等运行完了,再释放锁。 from threading import Lock total = 0 # 声明一把锁 lock = RLock() def add(): global lock global total for i in range(1000000): # 获得锁 lock.acquire() total += 1 # 释放锁 lock.release() def desc(): global total global lock for i in range(1000000): lock.acquire() total -= 1 lock.release() import threading thread1 = threading.Thread(target=add) thread2 = threading.Thread(target=desc) thread1.start() thread2.start() thread1.join() thread2.join() print(total) 打印结果: 0 上边代码不管累加多少次最终结果都是0。 如果没有释放锁,会导致死锁。 使用锁会影响性能。 RLock: 在同一个线程里面,可以连续调用多次acquire, 一定要注意acquire的次数要和release的次数相等。 在同一个线程中,可以使用RLock。

优秀的个人博客,低调大师

Python高级知识点学习(五)

dict的子类 首先,不建议自己编写代码继承dict和list这种原生使用c语言编写的,因为有时候,用c语言写的dict不会调用python写的覆盖的方法。 如果确实有继承dict来写代码的需求,可以使用UserDict,继承这个UserDict。 UserDict这个内部使用了python语言实现了c语言写的逻辑。 from collections import UserDict class Mydict(UserDict): def __setitem__(self, key, value): super().__setitem__(key, value*2) my_dict = Mydict(one=1) print (my_dict) 打印结果: {'one': 2} set和fronzenset set集合: 无序 不重复 set 接受一个可迭代对象 frozenset集合: 一旦设置好就无法修改。 frozenset为不可变类型。 相对于可变类型来说的好处,可以作为dict的key。 set的初始化方法: set([a, b, c]) a = {a, b, c} 两种都可以初始化 dict、set实现原理 当我们了解了背后的实现原理,就可以判断什么情况下使用dict以及为什么要使用dict。 dict查找的性能远远大于list。 在list中随着list数据的增大 查找时间会增大。 在dict中查找元素不会随着dict的增大而增大。 dict原理实际上就是利用hash算法。 数组和链表相比来说最大的优势,就是它可以做到任何一个位置直接存取而不需要从头到尾遍历,因为数组是一段连续的空间,数组取数据的时间复杂度是O(1)。 dict的key或者set的值,都必须是可以hash的。不可变对象都是可hash的, str, fronzenset, tuple。 自己实现的类重载__hash__这个魔法函数,让它可以变为可哈希对象。 dict的内存花销大,但是查询速度快, 自定义的对象或者python内部的对象都是用dict包装的。 dict的存储顺序和元素添加顺序有关,添加数据有可能改变已有数据的顺序。 Python中的变量是什么 python和java中的变量本质不一样,python的变量实质上是一个指针。我们可以理解变量就是一个便利贴, 例如:a = 1 先成对象,然后贴便利贴,把a贴在1上面。 a = [1, 2, 3] b = a print(id(a), id(b)) 打印结果: a和b的地址值一样。 is 和 == 的区别 is是判断对象的id是否相同,但是注意看下边例子: a = [1, 2, 3, 4] b = [1, 2, 3, 4] print (id(a), id(b)) print (a is b) 输出结果: 4446597320 4446597640 False 上边这种用法得到的结果很正常,再看下边代码: a = 1 b = 1 print (id(a), id(b)) print (a is b) 运行结果: 4325627840 4325627840 True 这种情况下,a和b指向的是同一个。这是由于Python内部机制决定的,将小正数建立一个全局唯一的对象,小段字符串也是一样的。 a = [1, 2, 3] b = [1, 2, 3] print(a==b) 返回True 因为: list里边实现了一个魔法函数 __eq__,当我们调用a==b这种模式的时候,会调用__eq__这个魔法函数,从而来判断值是否相等。 它们的值是相等的,只是不是同一个对象而已,所有a==b是True。 垃圾回收和del语句 cpython中垃圾回收的算法是采用 引用计数 a = 1 b = a 此时,1这个对象上就又有一个计数器,a = 1 时会在计数器上加1,b如果指向的还是1,1上边的计数器会再加1,当不使用时,执行del a,他就会将引用计数器减1,当引用计数器减到0时,python解释器会将对象回收。 对象只有在计数器减到0时,才会被回收,del只是减计数器的功能。 a = object() b = a del a print(b) print(a) 打印结果: <object object at 0x104a86100> NameError: name 'a' is not defined 可以看到,执行del后,只是把a销毁了,b还在。 __del__魔法函数: 可以在__del__魔法函数中实现自己的逻辑,当python解释器回收对象的时候,会调用对象的__del__魔法函数,它可以帮我们在回收对象时做一些事。 @property的用法 我们在读源码时,往往会看到这这种方法: @property def hello(self): pass @property 这个装饰器会把函数变为属性描述符,怎么说? 看代码: class Allen(object): def word(self): return 'word' @property def hello(self): return 'hello' a = Allen() print(a.hello) print(a.word()) 运行结果: hello word 可以发现,@property这个装饰器把取方法的模式变为取属性。 魔法函数__getattr__、__getattribute__介绍 魔法函数是python解释器内部需要用的方法,它是整个python动态特性的最根本原因。 __getattr__:就是在查找不到属性的时候调用。 例1: class User: def __init__(self, info): self.info = info def __getattr__(self, item): return '2' if __name__ == "__main__": user = User('allen') print(user.info) print(user.age) 运行结果: allen 2 例2: class User: def __init__(self, info={}): self.info = info def __getattr__(self, item): return self.info[item] if __name__ == "__main__": user = User(info={"name": "allen", "age": "3"}) print(user.name) 打印结果: allen __getattribute__ : 比__getattr__更高级,只要查找属性,就会首先进入__getattribute__这个魔法函数,强制进入,无条件的。 __getattribute__这个魔法函数尽量不要去重写,因为如果一旦写不好,整个类的属性访问就会崩溃掉,一般写框架时会用到这个魔法函数。 class User: def __init__(self,info={}): self.info = info def __getattr__(self, item): return self.info[item] def __getattribute__(self, item): return "10" if __name__ == "__main__": user = User(info={"name":"allen"}) print(user.name) print(user.test) 打印结果: 10 10 属性描述符和属性查找过程 一个类只需要实现__get__、__set__、__delete__这三个中的任意一个方法,它就算是属性描述符。 通过属性描述符,可以控制在赋值的时候它的行为,在属性设置的时候参数检查。 属性描述符有两种: 数据属性描述符:实现__get__ 和 __set__就是数据属性描述符。 非数据属性描述符:只实现一个__get__方法就是非数据属性描述符。 数据属性描述符 和 非数据属性描述符 它们的属性查找过程是不一样的。 前边提到的属性查找过程,先查找实例中的属性,然后查找类中的属性,实际上它有更加详细的查找过程。 魔法函数__new__ 和 __init__ 区别 __new__ 魔法函数在python新式类才会有 python2.2之前没有这个。 class User: def __new__(cls, *args, **kwargs): print(" in new ") return super().__new__(cls) def __init__(self): print(" in init") pass if __name__ == "__main__": user = User() 打印结果: in new in init __new__魔法函数允许在生成对象之前加逻辑,自定义对象生成过程,传递进来的是类。 __init__方法传递进去的是对象。 __new__ 在 __init__ 之前调用。 __new__中必须return super().__new__(cls)才会调用__init__方法。 def __new__(cls, *args, **kwargs):这个中的*args,和**kwargs,代表的是传入的参数。

优秀的个人博客,低调大师

Python高级知识点学习(三)

mro算法 类属性和实例属性的查找顺序 何为类属性:定义在类内部的的一些变量或者方法,都统称为类属性 何为实例属性:定义在对象内部的的一些变量或者方法,都统称为实例属性 对象也就是实例的意思。 class A: aa = 1 def __init__(self, x, y): self.x = x self.y = y a = A(2, 3) 类也是对象,看上边代码,实际上有两个空间,A 和 a 两个不同的空间。单继承时,属性查找方式,向上查找,首先查找对象里,再查找类中 在多继承时,会很复杂 python2.2之前,python里的类叫经典类,经典类继承方式如果不显式继承object,实际上是不会自动继承object,Python3中,经典类已经不存在了,都叫做新式类。经典类中,深度优先查找 。 Python2.3之后,广度优先也没有了,至今都采用C3算法Python3多重继承C3算法: #新式类 class D: pass class E: pass class C(E): pass class B(D): pass class A(B, C): pass print(A.__mro__) 打印结果:(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)可以看到继承顺序是 A - B - D - C - E - object 类方法静态方法和实例方法 实例方法:实例方法很常见,通常我们在类里定义的都是实例方法, 只是针对于实例进行操作,实例方法中第一个参数:self,self代表的就是实例本身 静待方法: 带@staticmethod装饰器的方法叫静态方法,静态方法不需要接受cls或self,和普通的函数用法一样 类方法: 带@classmethod装饰器的方法叫做类方法,类方法第一个参数是cls,代表的是类本身,(这里的cls可以修改为任意形式的代表) def a(self): pass @staticmethod def b(): pass @classmethod def c(cls): pass Python中的私有属性 双下划綫开头表示私有属性,私有属性的访问,只能在类中的公共方法中访问,类外部防问不到,无法通过实例访问。 私有属性不仅仅是变量 还可以是函数。 class User: def __init__(self, birthday): self.__birthday = birthday user = User(2000) print(user.birthday) 运行结果: AttributeError: 'User' object has no attribute 'birthday' 以上代码块结果就是访问不到私有属性。 但是,私有不是绝对的,只是加了一个小技巧,Python中将私有属性的访问变形成这种: class User: def __init__(self, birthday): self.__birthday = birthday user = User(2000) print(user._User__birthday) 运行结果: 2000 可以看到,通过变量_User__birthday这个就可以访问到私有属性。 Java中的反射机制也是无法做到绝对安全的,从语言层面讲,没有绝对的私有属性,Python简单一些 Java麻烦一些。 Python自省 Python对象自省何为自省?自省是通过一定的机制查询到对象的内部结构使用__dict__魔法函数,dict是用C语言写的 性能高,做了很多优化,推荐使用。 也可以使用dir() ,dir()会列出类中所有属性,推荐使用。 class Student(): def __init__(self, scool_name): self.scool_name = scool_name if __name__ == "__main__": user = Student("zhao") #通过__dict__查询属性 print(user.__dict__) 打印结果: {'scool_name': 'zhao'} class Student(): def __init__(self, name): self.name = name if __name__ == "__main__": user = Student("zhao") user.__dict__["school_addr"] = "北京市" print(user.school_addr) print(user.name) a = [1, 2] print(dir(a)) print(dir(user)) 打印结果: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'school_addr', 'scool_name'] ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] Python中super()函数 super()就是调用父类。 class A: def __init__(self): print('is A') class B(A): def __init__(self): print('is B') # 在某些情况下,我们希望在运行完以上代码调用父类的init方法,一般在Python3中使用下面这种方法调用 super().__init__() if __name__ == "__main__": b = B() 打印结果: is B is A super()就是调用父类,其实这样讲并不准确,super()函数调用其实是按照mro查找的顺序调用的。 class A: def __init__(self): print("A") class B(A): def __init__(self): print("B") super().__init__() class C(A): def __init__(self): print("C") super().__init__() class D(B, C): def __init__(self): print("D") super().__init__() if __name__ == "__main__": print(D.__mro__) d = D() 打印结果: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) D B C A 既然我们重写子类的构造函数, 为什么还要去调用super再次调用父类构造函数?有些时候,为了使用父类中的一些已经写好的方法,所以会有使用super的情况。 上下文管理器 首先介绍下这种用法: try: print("code started") raise KeyError return 1 except KeyError as e: print ("key error") return 2 else: print("other error") return 3 finally: print ("finally") return 4 result = exe_try() print (result) 运行结果: code started key error finally 4 在finally语句块中的代码,不管上边代码是否发生异常都会执行finally中的代码,优先finally中的return,其次return上边的。 上下文管理器,也就是with语句,实质上就是为了解放try finally这种写法而诞生的。 上下文管理器是如何完成的呢?python是基于协议进行编程的,上下文管理器就是一种协议:上下文管理器协议。 上下文管理器协议可以使用是因为实现了两个魔法函数:__enter__和__exit__ class Sample: def __enter__(self): print("enter") # 获取资源 return self def __exit__(self, exc_type, exc_val, exc_tb): # 释放资源 print("exit") def do_something(self): print("doing something") with Sample() as sample: sample.do_something() 运行结果: enter doing something exit 还有一种方法,可以简便的实现上下文管理器的功能:@contextlib.contextmanager 可以将一个函数变为上下文管理器 import contextlib @contextlib.contextmanager def file_open(file_name): print ("file open") yield {} print ("file end") with file_open("a.txt") as f_opened: print("file processing") 运行结果: file open file processing file end @contextlib.contextmanager内部会做一些逻辑,其实是利用了生成器的特性。

优秀的个人博客,低调大师

Python高级知识点学习(二)

深入类和对象 鸭子类型问:什么是鸭子类型?答:当看到一只鸟走起路来像鸭子,游泳像鸭子,叫起来也像鸭子,那么这只鸟就可以被看做鸭子。(所有的类或对象,都实现了共同的方法,方法名要一样,这样的话这些类就归为一种类型,在调用时同时调用同样的方法) 在java中,要实现多态,所有子类必须继承父类并重写父类的方法;在python中,python中对象和java不同,变量是动态的可以指向任何一个类型。 class Cat(): def say(): pass class Dog(): def say(): pass class Duck(): def say(): pass 三个类共同实现同一个方法且方法名相同,就实现了多态。Python多态比Java简单正是因为python是动态语言。 抽象基类abc模块 何为抽象基类?继续用java做比较,Python这里抽象基类可以当做java中的接口,java中是无法实现多继承的,java只能继承一个类,但是可以继承多个接口,接口是不能用来实例化的;所以Python里边抽象基类也是不能实例化的。 python是动态语言,动态语言是没有变量类型的,Python中变量只是个符号而已,它可以指向任何类型的对象。 所以在Python中也就不存在多态这种概念,我们可以赋值任何类型数据给Python中的变量,而且它是可以修改的,所以说它就不需要像java中一样去实现多态,因为本身从语言层面讲就是支持多态的语言。 动态语言和静态语言非常大的区别之一就是静态语言不需要我们指明变量类型,所以动态语言中也就少了编译时检查错误的环节,python中写错了代码只能在运行中发现错误。 不同魔法函数就赋予了类不同特性,和java非常大的区别就是不需要去继承某一个指定的类。 鸭子类型和魔法函数构成了Python语言的基础,也就是Python中的协议。我们只需要去实现指定的魔法函数,类就可以是某种类型的对象,不同魔法函数具有的特性不同,这种使用魔法函数事先约定好的做法我们都可以称之为一种协议。 抽象基类用途: 我们在某些情况之下希望判定某个对象的类型 我们需要强制某个子类必须实现某些方法 抽象基类用法: 在基础类中,我们去设定好一些方法,所有继承这个基类的类都必须覆盖抽象基类的方法, 抽象基类是无法实例化的 所有的抽象基类继承的必须是metaclass=ABCMeta 在做框架编程时,往往我们需要使用框架者在使用类时必须实现类中的一些方法,这时候可以使用抽象基类,举了例子: class CacheBase(metaclass=abc.ABCMeta): @abc.abstractmethod def get(self, key): pass @abc.abstractmethod def set(self, key, value): pass class RedisCache(CacheBase): pass redis_cache = RedisCache() 运行结果:TypeError: Can't instantiate abstract class RedisCache with abstract methods get, set在实例化RedisCache()这个时,就开始报错了,错误提示让我们必须实现get set方法。 补全这两个方法后运行就不报错了 class RedisCache(CacheBase): def set(self, key, value): pass def get(self, key): pass redis_cache = RedisCache() type和isinstance区别 尽量使用isinstance判断类型,避免使用type产生误差is 和 == 符号 区别: is用法判断id是否相同 ==用法判断值是否相等 类变量和实例变量(对象变量) 先看代码: class A: # 类变量 aa = 1 def __init__(self, x, y): # self是类的实例 # 实例变量 self.x = x self.y = y # 上边赋值后 x y 都属于实例了 不再属于类了 a是实例 A是类 aa是类变量 类变量在所有实例中是共享的 a = A(2, 3) A.aa = 11 a.aa = 100 # a实例新建一个属性 把aa放到a实例中 a.aa和A.aa是独立的 print(a.x, a.y, a.aa) print(A.aa) b = A(3, 5) print(b.aa) 运行结果: 2 3 100 11 11 解释代码: 新建了一个名为A的类,类中新建类属性aa = 1类中新建构造函数,绑定两个变量x y。 属性查找方式,向上查找,首先查找对象里有没有,再查找类中有没有类变量和实例变量是独立的存在 类变量在所有实例中是共享的 a.aa = 100:a实例新建一个属性 把aa放到a实例中 a.aa和A.aa是独立的。 建议以上代码反复测试加以记忆。

优秀的个人博客,低调大师

Python高级知识点学习(一)

image.png Python中一切皆对象 和Java相比,Python的面向对象更加彻底。 函数和类也是对象,是python的一等公民。 代码和模块也可以称之为对象。 python中的类也是对象,类可以理解为模板,根据这个模板去生成我们的对象,可以动态修改类的属性。 何为一等公民? 可以赋值给一个变量 可以添加到集合对象中 可以作为参数传递给函数 可以当做函数的返回值 (生成器应用) type、object、class之间的关系 类是由type来生成的一个对象,object是所有类都要继承的最顶层的一个基础类,type也是一个类,同时也是一个对象。 看代码片段一: a = 1 b = "abc" print(type(1)) print(type(int)) print(type(b)) print(type(str)) 打印出的结果: <class 'int'> <class 'type'> <class 'str'> <class 'type'> 代码片段二: class Student: pass class MyStudent(Student): pass stu = Student() print(type(stu)) print(type(Student)) print(int.__bases__) print(str.__bases__) print(Student.__bases__)# 打印Student基类 print(MyStudent.__bases__)# 打印Student基类 print(type.__bases__) print(object.__bases__) # 最顶层的类的基类为空 print(type(object)) 打印出的结果: <class '__main__.Student'> <class 'type'> (<class 'object'>,) (<class 'object'>,) (<class 'object'>,) (<class '__main__.Student'>,) (<class 'object'>,) () <class 'type'> 可以看到,类是由type来生成的一个对象。 上述代码建议反复阅读练习。 Python中常见的内置类型 首先说明对象的三个特征: 身份:也就是地址,通过id()函数查看地址 类型:int、str等 值:每个对象都有自己的值 常见内置类型: None(全局只有一个),Python解释器在启动时会用None生成一个None对象。 a = None b = None print(id(a) == id(b)) 打印结果: Ture 可以看到a,b是指向同一个对象(id相同)。 数值类型:int float complex(复数) bool。 迭代类型:可用for进行遍历 序列类型:list、bytes、range、tuple、str、array 等 映射类型:dict 集合类型:set、frozenset 上下文管理器:with语句 其他:class、实例 等等等。 Python中的魔法函数 问:什么是魔法函数? 答:双下划线开头,双下划线结尾的这些函数,通常称之为魔法函数。 例如: image.png 一般魔法函数不要自定义,使用 Python 提供的即可。 使用 Python 提供的魔法函数,为了增强类的特性。 代码:使用魔法函数之前 class Company(object): def __init__(self, employee_list): self.employee = employee_list company = Company(["tom", "bob", "jane"]) for i in company.employee: print(i) 打印结果: a b c 代码:使用魔法函数之后 class Company(object): def __init__(self, employee_list): self.employee = employee_list def __getitem__(self, item): return self.employee[item] company = Company(["a", "b", "c"]) 打印结果: a b c 可以看到__getitem__()这个魔法函数的功能。 定义了这个魔法函数后,实例化后的对象就隐含的变为可迭代对象(iterable)。 for循环其实是要拿到一个对象的迭代器,迭代器是需要实现__iter__这个方法才会有迭代器特性,Python语法会做一些优化,如果拿不到迭代器,就会尝试去对象中找__getitem__这个方法,如果有的话就会调用这个方法,一次一次直到将数据取完。这是python语言本身解释器会完成的功能。 魔法函数调用不需要显示调用,解释器会隐式调用。 Python的数据模型 魔法函数是Python数据模型的一个概念而已,因为网络上大家喜欢称之为魔法函数。 Python数据模型会增强对象的特性。 class Company(object): def __init__(self, employee_list): self.employee = employee_list def __getitem__(self, item): return self.employee[item] def __len__(self): return len(self.employee) company = Company(["a", "b", "c"]) print(len(company)) 结果: 3 __len__魔法函数增强了company对象的特性。 因为魔法函数很多,类似的魔法函数请大家自行去网上查找,并查看用法。

优秀的个人博客,低调大师

JavaScript—Ajax基础知识梳理(29)

Ajax用一句话来说就是无须刷新页面即可从服务器取得数据。注意,虽然Ajax翻译过来叫异步JavaScript与XML,但是获得的数据不一定是XML数据,现在服务器端返回的都是JSON格式的文件。 完整的Ajax请求过程完整的Ajax请求过程 1.创建XMLHttpRequest实例2.发出HTTP请求 3.接收服务器传回的数据4.更新网页数据 下面先看一个红宝书上给出的发起Ajax请求的例子,API的用法在后面章节给出。 var xhr = new XMLHttpRequest(); // 创建XMLHttpRequest实例 xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ // 判断请求响应过程阶段,4 阶段代表已接收到数据 if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码 console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } } }; xhr.open('get', 'example.txt', true); // 初始化xhr实例,或者说启动请求 xhr.send(null); // 设置HTTP请求携带参数,null为不带参数 Ajax请求过程详解 创建XMLHttpRequest实例从上面的的代码可以看出,创建一个XHR实例方式为: var xhr = new XMLHttpRequest(); 发出HTTP请求实例创建好后,首先需要启动一个HTTP请求,使用XHR的open()方法,open方法接受三个参数 XMLHttpRequest.open(method, url, isAsync) // 例如 xhr.open('get', 'http://www.baidu.com', true) 第一个参数为http请求使用方法,如('get','post'等),第二是参数是请求的url, 第三个参数代表是否异步发送请求(可选)。调用open()方法后会启动一个http请求,但它不会立即发送请求,处于待命状态。需要注意的是:请求的url必须要跟请求源域(origin)同域,也就是说协议、域名、端口号要一致,跨域请求要使用别的方法。接着调用send()方法就会发出这个http请求。 xhr.open('get', 'http://www.baidu.com', true) xhr.send(null) send()方法接受一个参数,为http请求发送的数据(通常用于'post'方法),如果为null,表示不发送数据。至此,一个异步的http请求就发送到了服务器。 接收服务器传回的数据3.1 发送同步请求 如果将open方法的第三个参数设为false,即为同步请求,当收到服务器的响应后,相应的数据会自动填充到XHR对象的属性中,主要包括以下四个: responseText:作为响应主体被返回的文本。 responseXML: 响应返回的XML文档,能接收到的前提是,响应的Content-Type字段的值为text/xml或者application/xml。 status: HTTP状态码。 statusText: HTTP状态码说明。 当客户端收到以上信息后,首先要判断HTTP状态码来确认响应是否成功,状态码在200-300之间表示请求成功,同时304代表请求资源未被修改,可使用浏览器本地缓存。如果成功就可以获取响应报文主体中的数据了。 xhr.open('get', 'http://www.baidu.com', false) xhr.send(null) if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码 console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } 3.2 发送异步请求如果将open方法的第三个参数设为true,即为异步请求。那么就需要一个事件来通知程序异步请求的结果是否返回。XHR对象中的readyState属性,表示请求/响应整个过程所处的阶段,它有五个值分为对应五个阶段: 0:未初始化。未调用open()方法。1:启动。已经调用open()方法,但未调用send()方法。2:发送。已调用send()方法,但未收到响应。3: 接收。已经接收到部分响应数据。4:完成。已经接受到全部响应数据。 readyState的值每变化一次,都会触发一次readStatechange事件,我们定义一个事件处理函数onreadStatechange(),并监听readyState == 4状态,就可以得知响应数据已全部收到,并进行下一步操作。那么就是文章开头给出的代码: var xhr = new XMLHttpRequest(); // 创建XMLHttpRequest实例 xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ // 判断请求响应过程阶段,4 阶段代表已接收到数据 if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码 console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } } }; xhr.open('get', 'example.txt', true); // 初始化xhr实例,或者说启动请求 xhr.send(null); // 设置HTTP请求携带参数,null为不带参数 补充XHR中三个有用的事件 timeout事件当超出了设置时间还未收到响应,就会触发timeout事件,进而调用ontimeout事件处理程序。同时timeout也是XHR的一个属性,用于设置这个时间阈值。下面是用法: xhr.ontimeout = function() { alert('timeout!') } xhr.open('get', 'http://www.baidu.com', true) xhr.timeout = 1000 // 时间阈值设为1秒 xhr.send(null) load事件 load事件用于简化对readState值的判断,响应数据全部接收完毕后(也就是readState == 4)会触发load事件,使用onload事件处理函数进行后续操作,onload会接收一个event对象,它的target属性等于XHR对象,当然我们在定义这个事件处理函数时也可以不传入这个参数,来看下面的用法: var xhr = new XMLHttpRequest() xhr.onload = function () { if(xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } } xhr.open('get', 'http://www.baidu.com', true) xhr.send(null) 这样就不用去关心readyState值的变化情况了。当然如果想在特定readyState值上做一些逻辑处理,还是要用之前的方法。 progress事件这个是很有用的一个事件,progress事件会在浏览器接收数据期间周期触发,代表整个请求过程的进度,它的事件处理程序onprogress接收一个event对象,event.target是XHR对象,另外event还有三个属性: lengthComputable:Boolean值,进度信息是否可用。position:已经接收到的字节数。totalSize:总共要接收的字节数,被定义在响应报文的Content-Length字段中。 如果响应报文中有Content-Length字段,那么我们就可以计算当前时刻响应数据的加载进度了,这也是之前看到的一个面试题。看下面的代码: xhr.onprogress = function(event) { if(event.lengthComputable) { console.log(`Received: ${(event.position/event.totalSize).toFixed(4)*100}%`); } } 其他还有很多有用的API,如FormData表单序列化,overrideMimeType()重写XHR响应的MIME类型等等

优秀的个人博客,低调大师

Python Flask学习知识点(七)

timg (11).jpg 静态文件访问原理 app文件夹下新建文件夹static,放入一张图片demo.jpg,然后访问服务器启动默认地址127.0.0.1:81/static/demo.jpg就可以访问到这张图片。 问:为什么Flask可以在任何代码不编写的情况下自动读取到static目录下的图片文件? 答:因为Flask对于静态文件是有默认值设置的,默认情况下,Flask认为静态文件夹的名字就叫ststic,并且位于应用程序的根目录下的,也就是app文件夹目录下。 image.png 问:为什么应用程序根目录是app目录而不是app上边的flaskDemo目录? 答:因为在实例化Flask核心对象是传入了一个参数,如下代码: def create_app(): app = Flask(__name__) app.config.from_object('app.secure') app.config.from_object('app.setting') register_blueprint(app) db.init_app(app) db.create_all(app=app) return app __name__这个参数决定了我们应用程序的根目录是app目录。 如果不想使用默认的static作为静态文件存储路径,可以通过更改配置来访问, app = Flask(__name__, static_folder='web/img') 更改成为这个路径,就要这样访问:127.0.0.1:81/img/demo.jpg, 还可以这么改: app = Flask(__name__, static_folder='web/img', static_path='') 但是我们一般还是使用Flask默认设置即可,如果确实有需求,具体使用详情请查阅官方文档。 静态文件放到蓝图中 之前说过,Flask里有两个层级: Flask核心对象:应用程序级别 蓝图 对于静态文件,也有两个级别: 应用程序static 蓝图static 如果你认为你的静态文件是针对你的蓝图的话,那么你其实是可以把静态文件注册到蓝图上边,而不是注册到应用程序上边。 image.png 蓝图的相关方法其实几乎就是复制了Flask核心对象的api,基本参数都是一致的。 蓝图注册静态文件夹: from flask import Blueprint # 蓝图 web = Blueprint("web", __name__, static_folder='', static_url_path='') 模板文件 模板就是html,但是和普通html不同,模板是没有数据的,它等待填充数据。 app文件夹下新建文件夹templates,新建html文件:test.html: image.png 同上边提到的static文件夹一样,我们可以自定义templates文件夹的位置,通过更改template_folder=参数来自定义templates文件夹的位置,例如,把templates文件夹移动到web文件夹下后需改动: 两种更改方法: Flask核心对象上更改:app = Flask(__name__, template_folder='web/templates') 在蓝图对象上更改:web = Blueprint("web", __name__, template_folder='templates') 现在要把视图函数中返回的数据填充到模板test.html中,使用Flask内置的方法:render_template() from flask import render_template @web.route('/test') def test(): r = { "name": "allen", 'age': 24 } r1 = { "name": "zhao", "age": 12 } return render_template('test.html', data=r, data1=r1) 模板语言 Jinja2 如何在模板中组织、解析相关数据 编辑test.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>demo</h1> {{data.age}} {{data1.age}} </body> </html> 运行代码,浏览器输入127.0.0.1:8000/test image.png 可以看到,之前我们写在视图函数中的数据已经展示到页面了。 Flask为了让我们很好的在模板解析和展示数据,它引入了一个模板引擎,叫做Jinja2。 Jinja2有自己的语法,例如注释代码:{#hello word#},这里就把hello word注释掉了。 我们的pycharm默认是不会像写python代码一样智能提示Jinja2的语法,但是,更改设置即可: image.png Jinja2也叫做模板语言, Flask官方推荐我们使用Jinja2,大家也可以自己使用其他的模板语言。

优秀的个人博客,低调大师

Python Flask学习知识点(六)

timg (10).jpg image.png Flask多线程机制 发送请求和服务器处理请求的线程之间的关系? 客户端发送十个请求,Flask开启多少个线程处理请求?其实是由web服务器开启的多线程。 如果要上生产线,一般不会用Flask自带的web server。 对于一个web网站而言,必须有承受并发的能力。 单线程: 如果是单线程,只有当一个请求处理完成之后,才会处理下一个请求,所以这时候看起来就像是要排队一样 单线程下,当一个请求进来之后,Flask会实例化一个Request对象,用这个对象来装载请求信息,这时候只有一个实例化的request,当第一个请求结束后,Flask会再实例化一个Request对象来装载第二个请求,然后request变量又指向当前请求,那么这样众多用户就不会混乱。 多线程: werkzeug库下local模块下的Local对象,Flask就是用Local来做线程隔离的。 Local对象的本质就是用线程的id号作为字典的键这种基本原理实现的。 Flask中线程隔离栈LocalStack LocalStack 是实现线程隔离的栈结构,把Local封装了。 LocalStack需要使用push方法 LocalStack: Local特性 Stack特性:栈结构,只能取栈顶元素,先进后出 使用线程隔离的意义在于:使当前线程能够正确引用到他自己做创建的对象,而不是引用到其他线程做创建的对象 线程隔离对象 LocalStack 问:Flask使用LocalStack为了隔离哪个对象? 答:被线程隔离对象 RequestContext AppContext Flask核心对象全局只有一个。 看一段测试代码,测试下LocalStack线程隔离: import threading from werkzeug.local import LocalStack stack = LocalStack() stack.push('a') print('主线程top:', stack.top) def child(): print("子线程:", stack.top) stack.push('b') print('子线程:', stack.top) t = threading.Thread(target=child, name='demo') t.start() print('主线程top:', stack.top) 打印结果: 主线程top: a 子线程: None 子线程: b 主线程top: a 还有哪些是被线程隔离的对象? image.png 以上都是被线程隔离的对象。 ViewModel的基本概念 很多时候,我们的数据在提交给页面的时候需要做一些处理或者裁剪,这时就需要ViewModel层来做这件事,如图: image.png viewModel作用 裁剪 修饰 合并 app文件夹下新建文件夹view_models,这里就可以处理数据的裁剪、修饰、合并。 单页面与网站的区别 一个网站页面由哪几部分构成? JS CSS(静态文件) HTML模板 数据 网站 一般来讲,服务器端把数据返回到HTML模板中,称之为服务器渲染,然后返回到浏览器给用户,此时,客户端浏览器网页中的JS CSS起作用再一次发起请求渲染页面,这是一个完整的页面就处理完了。 image.png 单页面 浏览器去请求一个静态的html,html返回到客户端浏览器后,html里边的js css 发起请求,来填充数据。 image.png 单页面和普通网站有什么区别: 区别的第一种说法: 对于多页面普通网站而言,大多数情况下它的数据渲染或者说模板填充都是在服务器端进行的, 而对于单页面来说,他数据的渲染是在客户端进行的。 区别的第二种说法: 单页面业务逻辑也就是说数据运算主要集中在客户端用js去操作,而普通网站而言,绝大多数业务逻辑是在服务器,也就是视图函数中去进行的。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册