python知识点

python学习

 

1. 函数参数(2020-3-3)

  1. args 可变参数 : 效果相当于传入参数 list或tuple,可以将list类型前面加一个 *使得list类型变为可变参数:nums表示把nums这个list的所有元素作为可变参数传进去。

  2. 关键字参数:效果相当于传入参数 dict ,可以将dict类型前面加一个**使得dict类型转换为关键字参数(实则是一份拷贝)

    • 命名关键字参数:对于关键字参数,限制关键字名字,用*进行分割, *后面的表示是关键字的key

  3. 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

  4. 对于任意函数,都可以通过类似func(*args, **kw)的形式调用它

  5. 默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误

2. 高级特性(2020-3-4)

  1. 切片:(可对string实现截取的操作,例如 list[2:1]不能执行左闭右开

    • 语法: str(start:end :step)

    • L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3

    • L[:3]

    • L[-2:]

    • L[-2:-1] 最后一个元素(-1)不会取得

    • L[:10:2] 前10个数,每两个取一个

    • L[:] 达到复制的功能

  2. 迭代:Iterable Itertor

  3. 列表生成式[ 生成元素 i * i for i in range(10) if i % 2 == 0 ]  

    • for循环的前部分是一个表达式(若为if必须加else),求具x的值求值

    • for循环的后部分是一个筛选条件(if不能加else)

  4. 生成器生成方法(两种)<yield>

  • 把一个列表生成式的[]改成(),就创建了一个generator,例如( i * i for i in range(10) if i%2 == 0)

    • 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

  1. 概念:

    • Iterable: 可作用于for循环

    • Iterator: 可作用与next对象,进行惰性计算,例如yield(生成器)

    • 集合数据类型如 list 、 dict 、 str 等是 Iterable但不是 Iterator ,不过可以通过 iter() 函数获得一个 Iterator 对象。Python的 for 循环本质上就是通过不断调用 next() 函数实现的

  2. map函数:接收两个参数,一个是函数,一个是 Iterable,返回一个Iterator,可用list()函数转换为list

  3. reduce函数:接受两个参数,一个是函数,一个是 Iterable,该函数必须接受两个参数,reduce函数功能是将函数值继续和序列的下一个元素做累积计算,例如reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4), 返回一个值

高级函数(2020-3-5)

  1. lambda函数:f = lambda x: x * x :前,函数参数,:后,函数主体,只能一个表达式

  2. filter函数:过滤函数返回为Ture的保留

  3. sorted函数:可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

    • sorted([36, 5, -12, 9, -21], key=abs)
  4. 返回函数:函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力

  5. 关于赋值语句:a, b = [1, 2] 左值数到等于右值数,否则报错

  6. 装饰器:

    • 装饰器不带参数: 用装饰器函数封装一个函数,所以函数就收一个函数为参数,返回一个装饰后的函数wrapper(一般)

    • 装饰器带参数复杂点,比不带参数的多了一层嵌套

  7. 偏函数:functools.partial就是帮助我们创建一个偏函数

    • int2 = functools.partial(int, base=2)
  8. 包和目录的区别:包相对于目录含有_init _.py文件

  9. 作用域:

    • _ _xxx _ _ 表示特殊变量

    • _xxx 非公开

面向对象编程 (2020-3-6)

  1. 普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。

  2. __xxx表示私有数据,实际转换为:类名 + _xxx

    _xxx表示不希望外部引用的数据

  3. “开闭”原则:

    • 对扩展开放:允许新增Animal子类;

    • 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数

  4. 鸭子类型:

    Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

  5. 相比于type类型,总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。

  6. dir:获取对象的所有属性和方法,配合getattr()setattr()以及hasattr()

  7. 定制类:

    • len():使用len()函数,函数自动去调用对象的__len__()方法

    • print():使用print函数,函数自动取调用对象的__str__方法

    • for...in...:要定义__iter__函数,首先返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值

    • list:要使得能像list一样下标引用,要实现对象的__getitem__方法

    • __getattr__:引用对象中没有的属性,才会去在__getattr__中查找是否有该属性

      class Student(object):
        def __getattr__(self, attr):
            if attr=='age':
                return lambda: 25
            raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
    • __call__():只需要定义一个__call__()方法,就可以直接对实例进行调用,即类中的__call__方法不用写成xx.__call__,而是直接xx()即可,能被调用的对象就是一个Callable对象,使用callable()函数可以判断某个变量是对象还是函数

    • __class__功能:__class__功能和type()函数一样,都是查看对象所在的类。

  8. 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的同名属性。

  9. s.name = 'Michael' # 动态给实例绑定一个属性
    s.set_age = MethodType(set_age, s) # 动态给实例绑定一个方法
    Student.set_score = set_score  #为了给所有实例都绑定方法,可以给class绑定方法
  10. 限制实例的属性和方法的动态添加:__slots__

    • __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称,,,使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
  11. @property使得方法变为属性,选择使用@xxx.setter使得方法增加setter,否则为只读属性

    • 使用@property使得类的属性不用增加getter和setter方法

    • 以后遇到类的属性就用@property语法,视情况增加@xxx.setter

  12. MixIn:(多重继承) 在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。MixIn设计要求有一个主继承,把额外功能写进另外的类里,加后缀MixIn进行标识。

    • 如何避免钻石继承(菱形继承)问题

      • Python 使用了一个叫“方法解析顺序(Method Resolution Order,MRO)”的东西,还用了一个叫 C3 的算法

  13. type动态创建类:python遇到class Xxx类都是调用type来创建类,type也是一个类

    • class的名称

    • 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;

    • class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

    • 例:Hello = type('Hello', (object,), dict(hello=fn))

  14. metaclass:先定义metaclass,就可以创建类,最后创建实例;可以把类看成是metaclass创建出来的“实例”

    • 实例化一个类的时候,具体的执行逻辑是这样的:

      • p = Person(name, age)

      • 首先执行使用name和age参数来执行Person类的__new__方法,这个__new__方法会 返回Person类的一个实例(通常情况下是使用 super(Persion, cls).__new__(cls, … …) 这样的方式)

      • 然后利用这个实例来调用类的i__init__ nit方法,上一步里面__new__产生的实例也就是 __init__ 里面的的 self.

    • __init____new__ 最主要的区别在于:

      1. __init__通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。

      2. __new__ 通常用于控制生成一个新实例的过程。它是类级别的方法。

      3. __new__ 至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

      4. __new__ 必须要有返回值,返回实例化出来的实例,这点在自己实现__new__ 时要特别注意,可以return父类__new__ 出来的实例,或者直接是object的__new__ 出来的实例

      5. 可以将类比作制造商,__new__ 方法就是前期的原材料购买环节,__init__ 方法就是在有原材料的基础上,加工,初始化商品环节

      6. __new__实现单例模式

  15. 对元类的理解与注意事项 元类就是类的类,python中函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。Python中所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。type就是Python的内建元类,当然了,也可以创建自己的元类。

  16. Python的错误其实也是class,所有的错误类型都继承自BaseException

  17. import logging
    logging.basicConfig(level=logging.INFO) #debug,info,warning,error等级
    s = '0'
    n = int(s)
    logging.info('n = %d' % n)
    print(10 / n)

测试与IO编程 (2020 3 7)

  1. def __setattr__(self, key, value):
          self[key] = value #???
  • 断言(继承unittest.TestCase):

    • self.assertEqual(abs(-1), 1) #断言函数返回的结果与1相等

    • with self.assertRaises(KeyError): #期待抛出指定类型的Error value = d['empty']

  • 运行单元测试:python -m unittest mydict_test

  • setUptearDown这两个方法会分别在每调用一个测试方法的前后分别被执行

  • 文档注释:(自动提取注释中的代码执行)

    • import doctest doctest.testmod()

  1. 文件操作:

    • 打开文件

      • open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
    • 操作文件

      • read(size):一次读取文件size字节内容到内存

      • readline():一次读取一行

      • readlines():一次读取所有,按行组成的list返回

    • 关闭文件

  2. StringIO:操作的不是文件,是str(你需要对获取到的数据进行操作,但是你并不想把数据写到本地硬盘上,这时候你就可以用stringIO)

  3. BytesIO:操作的是二进制数据

  4. 操作文件和目录:

    • os.name   os.uname()     os.environ
      # 1.查看当前目录的绝对路径:
      os.path.abspath('.')
      '/Users/michael'
      # 2.在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
      os.path.join('/Users/michael', 'testdir')
      '/Users/michael/testdir'
      # 3.然后创建一个目录:
      os.mkdir('/Users/michael/testdir')
      # 4.删掉一个目录:
      os.rmdir('/Users/michael/testdir')
      # 5.将两个目录合并为一个
      os.path.join()
      # 6.将目录拆分为两部分,后一部分总是最后级别的目录或文件名
      os.path.split('/Users/michael/testdir/file.txt')
      # 7.可以直接让你得到文件扩展名,很多时候非常方便
      os.path.splitext()
      # 8.对文件重命名:
      >>> os.rename('test.txt', 'test.py')
      # 9.删掉文件:
      >>> os.remove('test.py')
      # 10.shutil模块提供了copyfile()的函数,你还可以在shutil模块中找到很多实用函数,它们可以看做是os模块的补充。
  5. 序列化:变量从内存中变成可存储或传输的过程

    • pickle.dumps(d):把任意对象序列化为一个bytes <==> pickle.loads()

    • pickle.dump(d, f):直接把对象序列化后写入一个file-like Object <==>pickle.load()

    • 通用序列化:将类实例化要传入参数default=lambda obj: obj.__dict__,json反序列化要传入参数object_hook

      • json.dumps 序列化为str <==> json.loads

        • print(json.dumps(s, default=lambda obj: obj.__dict__))
      • json.dump 序列化为file-like-Object <==>

    • 对中文进行JSON序列化时,json.dumps()提供了一个ensure_ascii参数,设置为False可以显示中文

  6. 进程线程

  7. datetime:

    • #获取当前日期和时间
      now = datetime.now()
      #获取指定日期和时间
      dt = datetime(2015, 4, 19, 12, 20)
      #datetime转换为timestamp
      datetime(2015, 4, 19, 12, 20).timestamp()
      #timestamp转换为datetime
      datetime.fromtimestamp(1429417200.0)
      #str转换为datetime
      datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
      #datetime转换为str
      now.strftime('%a, %b %d %H:%M')
      #datetime加减
      now + timedelta(hours=10)
  8. collections:

    • namedtuple:

      Point = namedtuple('Point', ['x', 'y'])
      >>> p = Point(1, 2)
    • deque是为了高效实现插入(append, appendleft)和删除(pop, popleft)操作的双向列表,适合用于队列和栈

    • defaultdict:引用的Key不存在,如果希望key不存在时,返回一个默认值

      dd = defaultdict(lambda: 'N/A')

    • OrderedDict:orderedDict的Key会按照插入的顺序排列,不是Key本身排序

    • ChainMap:用ChainMap实现参数的优先级查找

    • Counter:

常见内建模块 (2020 3 9)

  1. Base64:是一种用64个字符来表示任意二进制数据的方法。

2020 3 10

  1. struct:二进制数据和bytes的转换

  2. hashlib:常见算法:md5,sha1

  3. hmac:使用方法如下:需要注意传入的key和message都是bytes类型,str类型需要首先编码为bytes,两种方法,1:b'abc' 2:‘abc'.encode('utf-8')

    import hmac
    message = b'Hello, world!'
    key = b'secret'
    h = hmac.new(key, message, digestmod='MD5')
    # 如果消息很长,可以多次调用h.update(msg)
    h.hexdigest()
    'fa4ee7d173f2d97ee79022d1a7355bcf'
  4. itertools:返回迭代器

    • count(start, step) 1, 3, 5, 6, ...

    • cycle((4, -4)) 4, -4, 4, -4, ...

    • repeat(1) 1, 1, 1, ...

    • chain() itertools.chain('ABC', 'XYZ'): # 迭代效果:'A' 'B' 'C' 'X' 'Y' 'Z'

    • groupby('AAABBBAAA', lambda c: c.upper())把迭代器中相邻的重复元素挑出来放在一起:

  5. contextlib:实现上下文管理是通过__enter____exit__这两个方法(返回自身)实现的

    • @contextmanager:希望在某段代码(比如一个函数)执行前后自动执行特定代码

      from contextlib import contextmanager
      class Query(object):
      
          def __init__(self, name):
              self.name = name
      
          def query(self):
              print('Query info about %s...' % self.name)
      @contextmanager
      def create_query(name):
          print('Begin')
          q = Query(name)
          yield q
          print('End')
      代码执行顺序:
      with语句首先执行yield之前的语句,因此打印出<h1>;
      yield调用会执行with语句内部的所有语句,因此打印出hello和world;
      最后执行yield之后的语句,打印出</h1>
  6. urllib:urllib提供的功能就是利用程序去执行各种HTTP请求。如果要模拟浏览器完成特定功能,需要把请求伪装成浏览器。伪装的方法是先监控浏览器发出的请求,再根据浏览器的请求头来伪装,User-Agent头就是用来标识浏览器的。

  7. request库

  8. chardet库:chardet.detect(b'Hello, world!')

  9. psutil库:系统运维工具

  10. virtualenv

    • 1. virtualenv --no-site-packages venv  #得到一个纯净的python虚拟环境
      2. source venv/bin/activate   #有了venv这个Python环境,可以用source进入该环境,用该命令进入一个virtualenv环境时,virtualenv会修改相关环境变量,让命令python和pip均指向当前的virtualenv环境。
      3. deactivate    #退出当前的venv环境
  11. Tkinter:编写的Python代码会调用内置的TkinterTkinter封装了访问Tk的接口;Tk是一个图形库,支持多个操作系统,使用Tcl语言开发;Tk会调用操作系统提供的本地GUI接口,完成最终的GUI

  12. TCP编程

    • 客户端编程:

      1. 创建一个socket

      2. 创建连接

      3. 发送数据

      4. 接收数据

      5. 关闭连接

2020 3 12

  1. tcp编程

    • 服务端编程:

      1. 创建一个socket

      2. 绑定套接字

      3. 开始监听

      4. 接受请求,开启新线程处理

      5. 接受数据

      6. 发送数据

      7. 关闭连接

  2. 电子邮件编程

    • SMTP编程:

      • Python对SMTP支持有smtplibemail两个模块,email负责构造邮件,smtplib负责发送邮件。

        from email import encoders
        from email.header import Header
        from email.mime.text import MIMEText
        from email.utils import parseaddr, formataddr
        
        def _format_addr(s):
            name, addr = parseaddr(s)
            return formataddr((Header(name, 'utf-8').encode(), addr))
        
        msg = MIMEText('Hello,send by Python...', 'plain', 'utf-8')
        # 输入Email地址和口令:
        from_addr = input('From: ')
        password = input('Password: ')
        # 输入收件人地址:
        to_addr = input('To: ')
        # 输入SMTP服务器地址:
        smtp_server = input('SMTP server: ')
        # 添加主题
        msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
        msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
        msg['To'] = _format_addr('管理员 <%s>' % to_addr)
        msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()
        
        
        
        import smtplib
        server = smtplib.SMTP(smtp_server, 25) # SMTP协议默认端口是25
        server.set_debuglevel(1)
        server.login(from_addr, password)
        server.sendmail(from_addr, [to_addr], msg.as_string())
        server.quit()
        • 发送HTML邮件:方法很简单,在构造MIMEText对象时,把HTML字符串传进去,再把第二个参数由plain变为html就可以了

        • 发送附件

        # 邮件对象:
        msg = MIMEMultipart()
        msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
        msg['To'] = _format_addr('管理员 <%s>' % to_addr)
        msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()
        
        # 邮件正文是MIMEText:
        msg.attach(MIMEText('send with file...', 'plain', 'utf-8'))
        
        # 添加附件就是加上一个MIMEBase,从本地读取一个图片:
        with open('/Users/michael/Downloads/test.png', 'rb') as f:
            # 设置附件的MIME和文件名,这里是png类型:
            mime = MIMEBase('image', 'png', filename='test.png')
            # 加上必要的头信息:
            mime.add_header('Content-Disposition', 'attachment', filename='test.png')
            mime.add_header('Content-ID', '<0>')
            mime.add_header('X-Attachment-Id', '0')
            # 把附件的内容读进来:
            mime.set_payload(f.read())
            # 用Base64编码:
            encoders.encode_base64(mime)
            # 添加到MIMEMultipart:
            msg.attach(mime)
      • 插入图片:要把图片嵌入到邮件正文中,我们只需按照发送附件的方式,先把邮件作为附件添加进去,然后,在HTML中通过引用src="cid:0"就可以把附件作为图片嵌入了。如果有多个图片,给它们依次编号,然后引用不同的cid:x即可。

        把上面代码加入MIMEMultipartMIMETextplain改为html,然后在适当的位置引用图片

        msg.attach(MIMEText('<html><body><h1>Hello</h1>' +
            '<p><img src="cid:0"></p>' +
            '</body></html>', 'html', 'utf-8'))
      • 加密SMTP

      • 小结:使用Python的smtplib发送邮件十分简单,只要掌握了各种邮件类型的构造方法,正确设置好邮件头,就可以顺利发出。

        构造一个邮件对象就是一个Messag对象,如果构造一个MIMEText对象,就表示一个文本邮件对象,如果构造一个MIMEImage对象,就表示一个作为附件的图片,要把多个对象组合起来,就用MIMEMultipart对象,而MIMEBase可以表示任何对象。它们的继承关系如下:

        Message
        +- MIMEBase
           +- MIMEMultipart
           +- MIMENonMultipart
              +- MIMEMessage
              +- MIMEText
              +- MIMEImage
    • POP3收取邮件:

      • Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件

      • 收取邮件分两步:

        第一步:用poplib把邮件的原始文本下载到本地;

        第二部:用email解析原始文本,还原为邮件对象。

      • 通过POP3下载

        from email.parser import Parser
        from email.header import decode_header
        from email.utils import parseaddr
        
        import poplib
        
        # 输入邮件地址, 口令和POP3服务器地址:
        email = input('Email: ')
        password = input('Password: ')
        pop3_server = input('POP3 server: ')
        
        def guess_charset(msg):
            charset = msg.get_charset()
            if charset is None:
                content_type = msg.get('Content-Type', '').lower()
                pos = content_type.find('charset=')
                if pos >= 0:
                    charset = content_type[pos + 8:].strip()
            return charset
        
        def decode_str(s):
            value, charset = decode_header(s)[0]
            if charset:
                value = value.decode(charset)
            return value
        
        def print_info(msg, indent=0):
            if indent == 0:
                for header in ['From', 'To', 'Subject']:
                    value = msg.get(header, '')
                    if value:
                        if header=='Subject':
                            value = decode_str(value)
                        else:
                            hdr, addr = parseaddr(value)
                            name = decode_str(hdr)
                            value = u'%s <%s>' % (name, addr)
                    print('%s%s: %s' % ('  ' * indent, header, value))
            if (msg.is_multipart()):
                parts = msg.get_payload()
                for n, part in enumerate(parts):
                    print('%spart %s' % ('  ' * indent, n))
                    print('%s--------------------' % ('  ' * indent))
                    print_info(part, indent + 1)
            else:
                content_type = msg.get_content_type()
                if content_type=='text/plain' or content_type=='text/html':
                    content = msg.get_payload(decode=True)
                    charset = guess_charset(msg)
                    if charset:
                        content = content.decode(charset)
                    print('%sText: %s' % ('  ' * indent, content + '...'))
                else:
                    print('%sAttachment: %s' % ('  ' * indent, content_type))
        
        # 连接到POP3服务器:
        server = poplib.POP3(pop3_server)
        # 可以打开或关闭调试信息:
        server.set_debuglevel(1)
        # 可选:打印POP3服务器的欢迎文字:
        print(server.getwelcome().decode('utf-8'))
        # 身份认证:
        server.user(email)
        server.pass_(password)
        # stat()返回邮件数量和占用空间:
        print('Messages: %s. Size: %s' % server.stat())
        # list()返回所有邮件的编号:
        resp, mails, octets = server.list()
        # 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
        print(mails)
        # 获取最新一封邮件, 注意索引号从1开始:
        index = len(mails)
        resp, lines, octets = server.retr(index)
        # lines存储了邮件的原始文本的每一行,
        # 可以获得整个邮件的原始文本:
        msg_content = b'\r\n'.join(lines).decode('utf-8')
        # 稍后解析出邮件:
        msg = Parser().parsestr(msg_content)
        print_info(msg)
        # 可以根据邮件索引号直接从服务器删除邮件:
        # server.dele(index)
        # 关闭连接:
        server.quit()
  3. 数据库:

    • JSON格式

      [
          {"name":"Michael","score":99},
          {"name":"Bob","score":85},
          {"name":"Bart","score":59},
          {"name":"Lisa","score":87}
      ]
    • SQLite:

      • 使用Python的DB-API时,只要搞清楚ConnectionCursor对象,打开后一定记得关闭,就可以放心地使用。

      • 使用Cursor对象执行insertupdatedelete语句时,执行结果由rowcount返回影响的行数,就可以拿到执行结果。

      • 使用Cursor对象执行select语句时,通过featchall()可以拿到结果集。结果集是一个list,每个元素都是一个tuple,对应一行记录。

      • 如果SQL语句带有参数,那么需要把参数按照位置传递给execute()方法,有几个?占位符就必须对应几个参数,例如:

        cursor.execute('select * from user where name=? and pwd=?', ('abc', 'password'))
    • MySQL:

  4. HTTP格式:

    • HTTP GET请求的格式:每个Header一行一个,换行符是\r\n

      GET /path HTTP/1.1
      Header1: Value1
      Header2: Value2
      Header3: Value3
    • HTTP POST请求的格式:当遇到连续两个\r\n时,Header部分结束,后面的数据全部是Body。

      POST /path HTTP/1.1
      Header1: Value1
      Header2: Value2
      Header3: Value3
      
      body data goes here...
      Body的数据类型由Content-Type头来确定,如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。
      当存在Content-Encoding时,Body数据是被压缩的,最常见的压缩方式是gzip,所以,看到Content-Encoding: gzip时,需要将Body数据先解压缩,才能得到真正的数据。压缩的目的在于减少Body的大小,加快网络传输。)
  5. WCGI接口:

    • Web应用的本质就是:

      1. 浏览器发送一个HTTP请求;

      2. 服务器收到请求,生成一个HTML文档;

      3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器;

      4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。

    • WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求:

      def application(environ, start_response):
          start_response('200 OK', [('Content-Type', 'text/html')])#发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个Header用一个包含两个str的tuple表示。
          return [b'<h1>Hello, web!</h1>']
      #environ:一个包含所有HTTP请求信息的dict对象;
      #start_response:一个发送HTTP响应的函数。
    • 运行WSGI服务

     

    # server.py
    # 从wsgiref模块导入:
    from wsgiref.simple_server import make_server
    # 导入我们自己编写的application函数:
    from hello import application
    
    # 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
    httpd = make_server('', 8000, application)
    print('Serving HTTP on port 8000...')
    # 开始监听HTTP请求:
    httpd.serve_forever()
    • 使用Web框架:其实一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应。但是如何处理HTTP请求不是问题,问题是如何处理100个不同的URL。每一个URL可以对应GET和POST请求,当然还有PUT、DELETE等请求,但是我们通常只考虑最常见的GET和POST请求。一个最简单的想法是从environ变量里取出HTTP请求的信息,然后逐个判断:

      def application(environ, start_response):
          method = environ['REQUEST_METHOD']
          path = environ['PATH_INFO']
          if method=='GET' and path=='/':
              return handle_home(environ, start_response)
          if method=='POST' and path='/signin':
              return handle_signin(environ, start_response)
          ...

      但是这样代码可维护性极差,代码这么写没法维护的原因是因为WSGI提供的接口虽然比HTTP接口高级了不少,但和Web App的处理逻辑比,还是比较低级,我们需要在WSGI接口之上能进一步抽象,让我们专注于用一个函数处理一个URL,至于URL到函数的映射,就交给Web框架来做。

    • flask框架:

      from flask import Flask
      from flask import request
      
      app = Flask(__name__)
      
      @app.route('/', methods=['GET', 'POST'])
      def home():
          return '<h1>Home</h1>'
      
      @app.route('/signin', methods=['GET'])
      def signin_form():
          return '''<form action="/signin" method="post">
                    <p><input name="username"></p>
                    <p><input name="password" type="password"></p>
                    <p><button type="submit">Sign In</button></p>
                    </form>'''
      
      @app.route('/signin', methods=['POST'])
      def signin():
          # 需要从request对象读取表单内容:
          if request.form['username']=='admin' and request.form['password']=='password':
              return '<h3>Hello, admin!</h3>'
          return '<h3>Bad username or password.</h3>'
      
      if __name__ == '__main__':
          app.run()
    • 使用模板:

      • Jinja2:{{ name }}表示一个需要替换的变量。很多时候,还需要循环、条件判断等指令语句,用{% ... %}表示指令。

      • Mako:用<% ... %>${xxx}的一个模板;

      • Cheetah:也是用<% ... %>${xxx}的一个模板;

      • Django:Django是一站式框架,内置一个用{% ... %}{{ xxx }}的模板。

  6. 异步IO:

    • 协程

      def consumer():
          r = ''
          while True:
              n = yield r
              if not n:
                  return
              print('[CONSUMER] Consuming %s...' % n)
              r = '200 OK'
      
      def produce(c):
          c.send(None)
          n = 0
          while n < 5:
              n = n + 1
              print('[PRODUCER] Producing %s...' % n)
              r = c.send(n)
              print('[PRODUCER] Consumer return: %s' % r)
          c.close()
      
      c = consumer()
      produce(c)
    • asyncio:

2020 3 15

  1. 1、上传本地文件到服务器

    scp /path/filename username@servername:/path/

    例如scp /var/www/test.php root@192.168.0.101:/var/www/ 把本机/var/www/目录下的test.php文件上传到192.168.0.101这台服务器上的/var/www/目录中

     

    2、从服务器上下载文件

    下载文件我们经常使用wget,但是如果没有http服务,如何从服务器上下载文件呢?

    scp username@servername:/path/filename /var/www/local_dir(本地目录)

    例如scp root@192.168.0.101:/var/www/test.txt 把192.168.0.101上的/var/www/test.txt 的文件下载到/var/www/local_dir(本地目录)

     

    3、从服务器下载整个目录

    scp -r username@servername:/var/www/remote_dir/(远程目录) /var/www/local_dir(本地目录)

    例如:scp -r root@192.168.0.101:/var/www/test /var/www/

     

    4、上传目录到服务器

    scp -r local_dir username@servername:remote_dir
  2.  

posted @ 2020-04-08 18:37  aBridge  阅读(87)  评论(0)    收藏  举报