Python进阶学习

Posted zhengweijie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python进阶学习相关的知识,希望对你有一定的参考价值。

一、类的基本概念

类是一组具有相同属性和方法的所有对象的集合或者抽象,定义一个类使用关键字 class类里面的方法必须接收一个self参数,该参数表示创建的对象本身,其语法如下:

class test:
  name = ‘‘
  age = 12
  height = 165
  
  def like(self):
    print(这是一个方法)

 

对象是类的一个实体,类是对象的抽象;对象是客观世界存在的一个实体。 用类创建一个对象并调用内部的属性,如下:

class Cat:
  name = ‘‘
  def like(self):
    print(这是一个类的方法)
  def show(self):
    print(self.name)

cat = Cat()
cat.name = 属性赋值
cat.like()  #这是一个类的方法
cat.show()  # 属性赋值

 

每个类都默认存在一个__init__(self)   函数,称之为构造函数(初始化方法),用来创建对象,在创建对象的时候会自动调用。我们可以在创建对象的时候使用构造函数进行赋值:

class Stu:
  def __init__(self, _name, _age):
    print(这是一个构造函数)
    self.name = _name
    self.age = _age
  name = ‘‘
  age = ‘‘
  def show(self):
    print(self.name, self.age)

obj = Stu(小米, 18)   # 传参给构造函数进行赋值
obj.show()     # 小米 18

 

每个类都默认存在一个 __del__(self) 函数,称为析构函数(销毁时的方法),在对象被动(del 删除)或主动(程序执行完毕)销毁的时候,会自动调用。我们可以使用析构函数在对象即将销毁时执行某些逻辑。 

class Stu:
  def __del__(self):
    print(这是一个析构函数)

obj = Stu()   
del obj

 

类封装的目的:类内部的数据可以在外部直接访问,这种方式不安全。我们需要限制在类的外部不能随意访问属性,并对某些属性进行隐藏。类的变量分为私有变量和类变量

类的私有变量:在变量名前添加两个下划线开头,声明该属性为私有,不能在类的外部被使用或 访问。例如 __test = ‘这是私有属性’。如果必须在外部进行访问,可以使用 对象名._类名私有属性名  的方式进行访问 。对象名和类名之间有一个点和一个下划线,类名后面是私有属性名

类的私有方法类的私有方法跟私有变量一样,在方法名前加两个下划线开头 ; 在外部访问的方式也跟私有变量一样

class Stu:
  __test = 这是私有变量
  def get(self):
    return self.__test

obj = Stu()
print(obj._Stu__test)
print(obj.get())

类变量:指的是所有对象所共享的变量,类变量定义在类的定义之后,实例化前后都可以使用

类变量可以用 类名.类变量 和  self.类变量  两种方式访问,后者一般不建议使用。类变量是所有对象所共享的,无论任何时候都建议用类名的方式访问类变量

class Stu:
  name = 这是类变量
  def test(self):
    print(self.name)     #  两种方式都可以访问
    print(Stu.name)      # 推荐方式

print(Stu.name)     # 外部使用类名也可以访问
obj = Stu()
obj.test()

 

实例变量:指的是在__init__(self) 方法内声明的变量。 实例变量必须在实例化类后才能使用。实例变量只能通过对象访问(self), 不能通过类名访问。

class Stu:
  def __init__(self, name, age):
    self.name = name           #  实例变量
    self.age = age

 

类的三种方法分为 静态方法、类方法、实例方法

实例方法:只能被实例对象调用,第一个参数必须默认传实例对象,一般习惯用self。  在实例方法内部调用另一个实例方法,使用 self.方法名

静态方法:使用 @staticmethod 修饰,对于参数没有要求,可以用 类名.方法名 和 self.方法名 两种方式访问。在静态方法内部调用另一个静态方法,使用 类名.方法名

类方法:  使用@classmethod 修饰, 第一个参数必须默认传类, 一般习惯用 cls  。可以用 类名.方法名 和 self.方法名 两种方式访问

class Stu:
  def __init__(self):
    print(这是一个实例方法)

  @staticmethod
  def statics():
    print(这是一个静态方法)

  @classmethod
  def classTest(cls):
    print(这是一个类方法)

 

继承的概念:一个类从另一个类中继承一些属性和方法,(无法继承私有的)我们把这个类叫做子类(派生类),另一个类叫做父类(基类)。其语法如下:class childrenClass(parentClass)

多继承:   一个子类可以同时继承多个父类,用 逗号隔开;如果继承的父类有相同的属性或者方法,则按照传入的顺序优先使用,先传先用。 

继承传递: 如果A类继承B类, B类继承C类, 那么默认A类继承C类。

类方法重写:  当子类继承的方法不适用的时候,可以进行重写。在子类内部定义一个跟继承方法相同名称的的方法,可以覆盖继承过来的方法。

class parent:
  name = 这是父类属性
  def show(self):
    print(parent.name)

class children(parent):
  types = 这是子类属性 
  def test(self):
    print(children.types)

obj = children()
obj.test()
obj.show()

 

抽象类:

抽象类是包含抽象方法的类,该类不能被实例化,而抽象方法不包含任何可实现的代码(常用pass语句),只能在其子类中实现抽象函数的代码(一般被子类继承,通过子类实例化后重写方法。

子类一定要实现重写抽象类的所有抽象方法,否则这个子类就是一个抽象类,不能被实例化。

python 本身中不存在抽象类、接口的概念,要实现这种功能需要abc.py这个类库。具体方式:在定义抽象类前需要从类库abc 导入 ABCmeta类(即Metaclass for defining ABstract BaseClasses, 抽象基类的元类)和abstractmethod类。

定义抽象类的步骤:

1、在定义抽象类时需要在类定义中加入如下代码: metaclass=abc.ABCMeta ,  即指定该类的元类是 ABCmeta , 所谓元类就是创建类的类。

2、在定义抽象方法时需要在前面加入如下代码:  @abstractmethod   ;  因为抽象方法不包含任何可实现的代码,因此函数体通常使用 pass 。

import abc
from abc import abstractmethod

class Test(metaclass=abc.ABCMeta):
  def __init__(self):
    print(做点什么)

  @abstractmethod
  def do(self):
    pass

 

接口:

接口是一种特殊的类。当抽象类中所有的方法都是抽象方法时,或者一个普通类所有的方法都没有实现逻辑,我们可以把这个类当成一个接口 Interface。

Python中并没有 Interface 这个关键字来定义一个接口,其实就是定义一个抽象类,个人可理解为 是为了规范子类中的所有方法一致性。

接口有两点要注意:

1、接口的实现类(子类)必须要有接口中所有的方法

2、接口的实现类(子类)实现的方法必须跟接口中所有的方法的名字一样

接口有两种实现方式:

import abc
from abc import abstractmethod

#使用抽象类实现,该类里面全是抽象方法
class Interface1(metaclass=abc.ABCMeta):
  @abstractmethod
  def show(self):
    pass

# 使用普通类实现,该类里面全是没有实现逻辑的方法
class Interface2:
  def show(self):
    pass

class Test(Interface1):
  def show(self):
    print(重写每一个方法,赋予逻辑)
    
class Test2(Interface2):
  def show(self):
    print(重写每一个方法,赋予逻辑)

 

多态:

所谓多态指的是程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才决定

简单理解:  一个函数的参数,通过传入参数的不同具备不同的意义;或者 + 号在不同的地方使用具备不同的意义,这就是多态。

多态的好处: OOP的开闭合原则,对添加代码开放,对修改代码闭合。

实现多态的方法: 1、使用继承;  2、使用接口

 

重载:

重新定义print输出的内容,使用__str__() 方法,该方法 return 回一个 输出的值。 在类里面定义该方法,直接输出实例对象,会输出 __str__() 方法返回的值

class Test:
  def __str__(self):
    return 这是重新定义输出的内容

test = Test()

print(test)    # 这是重新定义输出的内容

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种新的功能,以适应不同的数据类型。在一个类中具备了对运算符进行重载的多个方法,该方法可以重新定义两个实例对象相加或相减等运算的结果。

# encoding=utf-8

class Vector:
  def __init__(self, x, y):
    self.X = x
    self.Y = y
  # 重载print()方法
  def __str__(self):
    return "[{0}, {1}]".format(self.X, self.Y)
  # 重载 + 运算符
  def __add__(self,other):
    return Vector(self.X + other.X, self.Y + other.Y)
  # 重载 - 运算符
  def __sub__(self,other):
    return Vector(self.X - other.X, self.Y - other.Y)
  # 重载 * 运算符
  def __mul__(self,other):
    return Vector(self.X * other.X, self.Y * other.Y)
  # 重载 % 运算符
  def __mod__(self,other):
    return Vector(self.X % other.X, self.Y % other.Y)


v1 = Vector(5, 6)
print(v1)   # [5, 6]

v2 = Vector(5, 6)
v3 = Vector(3, 3)
print(v2 + v3)    # [8, 9]

v4 = Vector(5, 6)
v5 = Vector(3, 3)
print(v4 - v5)    # [2, 3]

v6 = Vector(5, 6)
v7 = Vector(3, 3)
print(v6 * v7)    # [15, 18]

v8 = Vector(5, 6)
v9 = Vector(3, 3)
print(v8 % v9)    # [2, 0]

自定义异常: 除了默认的异常类型,还可以使用 repr() 方法 在类里面自定义异常:

#encoding=utf-8

class MyException(Exception):
  def __init__(self, value):
    self.value = value
  def __str__(self):
    return repr(self.value)    # 自定义抛出异常信息

try:
  test = int(input(请输入年龄))
  if test > 130:
    raise MyException(年龄不能大于130)   # 自定义抛出异常
  if test < 1:
    raise MyException(年龄不能小于1)     # 自定义抛出异常
except ValueError as e:
  print(请输入数字)
except MyException as a:   # 使用自定义的异常类型
  print(a.value)

 

迭代器: 使用 iter()  方法, 使某个集合转换为迭代对象, 使用next() 依次输出里面的值。通常跟for 循环配合使用;

list1 = [1,2,3,4,5,6,7,8]
iter1 = iter(list1)

print(next(iter1))  # 1
print(next(iter1))  # 2

for t in iter1:
  print(t)

 

列表生成器: 通过列表生成式,我们可以直接创建一个列表。但是受到内存限制,列表容量肯定是有限的。而且当创建的列表包含100万个数据时,会占用很大的内存,如果我们仅仅使用到前几个数据,那么后面元素占用的空间就浪费了。所以,将列表元素按照某种算法推算出来,不必创建完整的list, 从而节省大量的空间,这种一边循环一边计算的机制,称为生成器(Generator) 。

创建生成器的方法: 把一个列表生成式的 []  改为 ()

list1 = [x for x in range(1,1000)]    #  列表生成式
list2 = (x for x in range(1,1000))    #  列表生成器
print(list2)

 

创建函数生成器的方法:在一个函数内定一个 yield 语句,那个这个函数就变成了 Generator 。 函数生成器和函数的执行流程不一样,函数是顺序执行,遇到return或者执行完毕就返回。而变成generator的函数,在每次调用next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的yield语句处继续执行。   如果在 yield 语句前先遇到return ,会直接停止迭代并抛出异常。

def sum():
  print(one)
  yield 1
  print(two)
  yield 2
  print(three)
  yield 3 
  print(end)

test = sum()
next(test)   # one
next(test)   # two
next(test)   # three
next(test)   # end  超过迭代长度,报错

 

 斐波那契数列的各种实现方法:

def fibo(n):
  a, b, n1 = 0, 1, 0
  while n1 < n:
    a, b = b, a + b
    print(a)
    n1 = n1 + 1

fibo(5)   # 1 1 2 3 5
import sys

class Fibo:
  def __init__(self, max):
    self.max = max
    self.a, self.b, self.n = 0,1,0
  def __iter__(self):        #迭代器,返回自己
    return self
  def __next__(self):
    if self.n < self.max:
      r = self.b
      self.a, self.b = self.b, self.a + self.b
      self.n = self.n + 1
      return r
    else:
      sys.exit(0)


for t in Fibo(5):
  print(t)      #  1 1 2 3 5

 

def fibo(n):
  a, b, n1 = 0, 1, 0
  while n1 < n:
    a, b = b, a + b
    yield a
    n1 = n1 + 1

for t in fibo(5):
  print(t)   # 1 1 2 3 5

   

如何判断一个函数是否一个generator函数,使用 isgeneratorfunction 模块:

from inspect import isgeneratorfunction

def fibo(n):
  a, b, n1 = 0, 1, 0
  while n1 < n:
    a, b = b, a + b
    yield a
    n1 = n1 + 1

print(isgeneratorfunction(fibo))    # True

 

生成器的应用场景:

1、遍历输出多维列表(扁平化列表)

list1 = [1,[2,3,4],5,6,[7,[8,9]]]

def flatten(list2):
  if type(list2) == list:
    for t in range(len(list2)):
      for e in flatten(list2[t]):
        yield e
  else:
    yield list2



for t in flatten(list1):
  print(t)

 

2、切割读取文件。直接对文件对象调用read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容:

def readFile(paths):
  block_size = 4096 * 2
  with open(paths, mode=r+) as f:
    while True:
      block = f.read(block_size)
      if block:
        yield block
      else:
        return

for t in readFile(test.txt):
  with open(new.txt, mode=a+) as f2:
    f2.write(t)

 

生成器支持的方法:

close() 方法: 手动关闭生成器,关闭后再次调用迭代会报异常

def test():
  yield 1
  yield 2
  yield 3
  yield 4

t = test()

print(next(t))  # 1
print(next(t))  # 2
t.close()
print(next(t))  # 报停止迭代异常

 

send() 方法: 用于向生成器传入某个参数,yield会根据参数返回特定的值,使用该方法第一次调用的时候必须传 None

def test():
  value = 0
  while True:
    r = yield value
    if r == stop:
      break
    value = 得到某个值进行操作并返回:%s%r

t = test()
print(t.send(None))   # 0   第一次调用必须传 None
print(t.send(a))    #  得到某个值进行操作并返回:a
print(t.send(b))    #  得到某个值进行操作并返回:b
print(t.send(stop)) #  停止迭代报异常

 

throw()  方法: 用于使生成器抛出指定的异常类型,或自定义异常类型。调用该方法的时候,会消耗下一次迭代。

def test():
  while True:
    try:
      yield 正常输出1
      yield 正常输出2
      print(迭代结束)
    except ValueError:
      print(值异常)
    except TypeError:
      break

t = test()
print(next(t))  # 正常输出1
print(next(t))  #正常输出2
t.throw(ValueError)   # 值异常
print(next(t))  #正常输出2
t.throw(TypeError)   # 抛出迭代结束异常

 

正则:

使用正则表达式需要引入 re模块 , 使用步骤如下:

compile(正则,模式)  方法:生成pattern实例;模式可传 re.I 或 re.M 等

re.I     不区分大小写,忽略大小写

re.M    多行匹配,影响 ^ 和 $

import re

#定义正则表达式, 编译成Pattern实例
p = re.compile(a.b)
#使用实例,获得匹配结果
test = re.match(p, a3b)

print(test.group(0))   # a3b

 

正则表达式字符含义(列举部分,更多查询文档):

 .     表示除了换行符\\n 外的任意字符

\\     表示转义,改变后面一个字符的原来意义

*     表示前一个字符可以为0个或者多个

+    表示前一个字符至少为1个或者多个

?   表示前一个字符为0个或者1个

^    表示以某个字符开头

$   表示以某个字符结尾

{m, n}   表示匹配次数, 表示最少m次。表示最多n次

[]    表示字符集中任意字符 例如 [123456798]

()    表示分组, 例如 (abc){2}

预定义字符集:

\\d :    表示 0 ~ 9

\\D : 表示除了 0 ~ 9 的所有字符,非数字

\\s : 表示任何空白字符

\\S :   表示除了空白字符的任意字符

\\w :   表示 [0-9A-Za-z]

\\W : 表示除了 [0-9A-Za-z]   即特殊字符

\\A:   表示以 某个字符开头,等价于 符号  ^

\\Z : 表示以某个

\\b :表示单词边界

 

匹配函数: 

match(pattern, string)  方法: 从字符串起始位置开始匹配,如果不是起始位置匹配成功的话,返回none ,如果匹配成功,返回Match 对象

#定义正则表达式, 编译成Pattern实例
p = re.compile(ab)
#使用实例,获得匹配结果
test = re.match(p, sabc)

print(test.group())   # none

 search (pattern, string) 方法: 对字符串进行整体匹配,找到第一个匹配的值后返回,后续的不再进行匹配。,返回Match 对象

#定义正则表达式, 编译成Pattern实例
p = re.compile(ab)

test = re.search(p, sabcabab)

print(test.group())   # ab

 findall(pattern,string):  以列表形式输出所有匹配的字符串,查找全部,返回列表

import re

#定义正则表达式, 编译成Pattern实例
p = re.compile(ab,re.M)

print(re.findall(p, abcdefgab))   # [‘ab‘, ‘ab‘]

finditer(pattern, string):  已迭代器形式返回所有匹配的字符串,查找全部,返回迭代器

p = re.compile(ab,re.M)
m = re.finditer(p, abcdefgab)
print(next(m).group())  # ab

 

sub(pattern, repl, string, count = 0):   根据正则替换字符串中指定字符 ;   repl 表示替换的新字符,string表示要被替换的字符串, count表示替换的次数,不传默认为 0 ,替换全部

import re

text = 1a2a3a4a5a6a7a
p = re.compile(a)
new = re.sub(p, ‘‘, text)
print(new)   #132467 

匹配函数传参:

pattren 表示匹配的正则表达式  ; 

string 表示需要匹配的字符串 ; 

flags 表示标志位,用于控制正则表达式的匹配方式,如 是否区分大小写、多行匹配等;可选

 

Match对象方法:

group()  方法:  返回匹配成功的字符串,不传参或传0 的情况下,返回匹配的所有字符串;当匹配的正则有分组时,依次传参1,2,3输出对应的正则组所匹配的字符

groups() 方法: 以元组形式返回所匹配的各个字符串,以分组形式, 没有分组的情况下返回()

start()   方法:  返回开始匹配的字符位置

end()    方法:   返回结束匹配的字符位置

span()  方法:   以元组形式同时返回开始匹配和结束匹配的位置

p = re.compile(ab)

test = re.search(p, sabcabab)

print(test.group())   # ab
print(test.groups())   # ()
print(test.start())   # 1
print(test.end())     # 3
print(test.span())   # (1, 3)
p = re.compile((ab)(cd)(ef))

test = re.search(p, abcdefg)

print(test.group())   # abcdef
print(test.group(1))   # ab
print(test.group(2))   # cd
print(test.groups())   # (‘ab‘, ‘cd‘, ‘ef‘)

 

TCP  面向连接(传输层通信协议):

1、在通信开始的时候要有一个建立连接的过程。

2、在通讯的整个过程中,有一个专用资源供双方通信使用,所有的数据都要经过这个专线,直到通信结束。

优势:数据不会丢失,也不会发生乱序的现象

劣势:建立连接的时候,消耗资源比较大

UDP 面向无连接(传输层用户数据报协议):

1、在通信的整个过程中,数据到达目的地有很多路径

优势:不需要建立连接,可以最大化利用网络资源

劣势:数据可能会丢失,会发生乱序的现象

 

socket编程

参考:https://www.cnblogs.com/wangcq/p/3520400.html

socekt又称为‘套接字’,用于描述IP和地址端口,是一个通信链路的句柄,应用程序通常通过套接字向网络发出请求或者应答网络请求。网络中进程之间的通信使用的就是socket。 socket = IP + 端口

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

以下为socket常用的几个接口函数:

sk.bind(address) :      (服务端)将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

sk.listen(backlog) :     (服务端) 开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5 这个值不能无限大,因为要在内核中维护连接队列

sk.accept() :              (服务端)被动接收TCP 客户的连接(阻塞式)等待连接的到来

sk.connect(address) : (客户端)主动初始化TCP服务器连接。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

sk.connect_ex(address) : (客户端)sk.connect() 函数的扩展版本,连接成功时返回 0 ,连接失败时候返回编码,不会抛出异常

sk.recv(bufsize [,flag]) :   (共用) 接收TCP数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

sk.send(string [,flag]) :      (共用)发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

sk.sendall(string[,flag]) :  (共用) 完整发送TCP数据,将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去。

sk.recvfrom(bufsize[.flag]) :       (共用)接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sk.sendto(string[,flag],address) :(共用) 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

sk.close():(共用)关闭套接字

socket(domain, type,  protocol) :的使用

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

  • domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
  • type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
  • protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP等,它们分别对应TCP传输协议、UDP传输协议。

注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。

当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。

socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAN)

bind() 函数

正如上面所说bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。

通常服务器在启动的时候都会绑定一个众所周知的地址(如 IP地址+端口号),用于提供服务,客户就可以通过它来连接服务器。而客户端就不用指定,有系统自动分配一个端口号和自身的 IP地址组合。这就是为什么通常服务器端在 listen 之前会调用 bind() ,而客户端就不会调用,而是在 connect() 时由系统随机生成一个。

socket_server.bind((host,9999))

 listen()、connect()函数

如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

 listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。 

socket_server.listen(5)
socket_client.connect((serverHost,9999)

accept()函数

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

 

socket的使用步骤:

TCP客户端:

# encoding=utf-8
import socket

# 创建一个socket对象  AF_INET表示IPv4  SOCK_STREAM表示TCP
socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 得到客户端主机名字
hosthome = socket.gethostname()
#连接服务端 该端口需与服务端保持一致
socket_client.connect((hosthome, 9999))
#收发数据  注意把字符串转换为字节流
sendData = hello Wrold
socket_client.sendall(sendData.encode(utf-8))
#关闭
socket_client.close()

 

TCP服务端:

import socket

# 创建一个socket对象  AF_INET表示IPv4  SOCK_STREAM表示TCP
socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 得到主机名字
serverHost = socket.gethostname()
#服务端绑定
socket_server.bind((serverHost, 9999))
#监听连接 此时服务端已经启动 此时处于阻塞状态,待接收请求
socket_server.listen(5)
#服务端接收来自客户端的请求  返回的是一个元组对象
tupleObj = socket_server.accept()
# 分别获得来自客户端请求的socket对象和客户端信息
socket_object, address_info = tupleObj
print(address_info) # (192.168.1.1, 53496)
# 获得接收到的数据 注意把字节流转换为字符串
reviceData = socket_object.recv(512)
print(接收到的数据为, reviceData.decode(utf-8))
#关闭
socket_object.close()
socket_server.close()

 

 

UDP服务端:不存在监听步骤

import socket

# 创建一个socket对象  AF_INET表示IPv4  SOCK_DGRAM表示UDP
udpserver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 服务器绑定 
udpserver.bind((127.0.0.1, 9999))
# 接收数据 并获得接收的数据内容和客户端请求地址
data, address_info = udpserver.recvfrom(512)
# 关闭
udpserver.close()

 

UDP客户端:不存在连接步骤

import socket

# 创建一个socket对象  AF_INET表示IPv4  SOCK_DGRAM表示UDP
udpclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 向服务端发送数据 
serverHost = (127.0.0.1, 9999)
udpclient.sendto(hello.encode(utf-8), serverHost)
# 关闭
udpclient.close()

 

模拟聊天程序案例:

#encoding=utf-8
import socket
socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientHost = socket.gethostname()
socket_client.connect((clientHost, 9999))

while True:
  sendData = input(请输入发送消息:)
  socket_client.sendall(sendData.encode(utf-8))
  # 接收回复消息
  reviceData = socket_client.recv(512)
  print(reviceData.encode(utf-8))
  if sendData == quit:
    break

socket_client.close()
# encoding = utf-8
import socket
socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverHost = socket.gethostname()
socket_server.bind((serverHost, 9999))
socket_server.listen(5)
socket_object, address_info = socket_server.accept()

while True:
  reviceData = socket_object.recv(512)
  if reviceData.encode(utf-8) != quit:
    print(接收到数据, reviceData)
    # 发送回复数据
    sendData = input(请输入回复消息:)
    socket_object.sendall(sendData.decode(utf-8))
  else:
    print(退出)
    break

socket_server.close()
socket_object.close()

 

传输文件案例:

#encoding=utf-8
import socket

# 打开并读取某个文件
f = open(1.png, mode="rb+")
fconten = f.read()

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((127.0.0.1, 9999))
client.sendall(fconten)
client.close()
# encoding = utf-8
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((127.0.0.1, 9999))
server.listen(5)
socketObj, addressInfo = server.accept()
# 接收传输过来的数据
data = socketObj.recv(1024*1000)
# 新建一个文件并写入
f2 = open(new.png, mode="wb+")
f2.write(data)

f2.close()
server.close()
socketObj.close()

 

传输大型文件案例:将文件切割依次传输

#encoding=utf-8
import socket

socketClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socketClient.connect((127.0.0.1, 9999))

# 打开读取一个文件
f = open(1.mp4, mode="rb")
# 设置每次读取的长度, 开始读取并发送
readfile = f.read(1024*1024)
socketClient.sendall(readfile)
#未读取完毕的情况下,循环读取并发送,为空时退出
while readfile:
  readfile = f.read(1024*1024)
  socketClient.sendall(readfile)

f.close()
socketClient.close()
#encoding=utf-8
import socket
socketServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socketServer.bind((127.0.0.1, 9999))
socketServer.listen(5)
socketObj, socketInfo = socketServer.accept()

# 创建一个新的文件
fnew = open(new.mp4, mode="wb")
# 循环不断接收并写入
while True:
  recvData =  socketObj.recv(1024*1024)
  # 接收的数据长度为0,退出
  if len(recvData) == 0:
    break
  fnew.write(recvData)
  
fnew.close()
socketObj.close()
socketServer.close()

 

 

邮件发送/接收的三大协议

SMTP: 简单邮件传输协议,用来发送或中转发出的电子邮件

POP3:用来接收电子邮件

IMAP:交互式邮件存取协议,跟POP3类似,用于接收电子邮件,使用该协议,在客户端上的操作都会反馈到服务器上,如删除、标记已读等,服务器端都会进行同步操作

 

发送纯文本邮件:

#encoding=utf-8
# 导入模块
import smtplib
import email
from email.mime.text import MIMEText
import traceback
try:
  # 设置邮件的会话对象
  session = smtplib.SMTP_SSL(smtp.qq.com, 465, 30)
  # 设置邮件信息(邮件内容、邮件类型、标题、发送人、接收人)
  message = MIMEText(这是一封普通的纯文本邮件, plain, utf-8)
  message[subject] = 纯文本    # 标题
  message[from] = [email protected]   # 发送人
  message[to] = [email protected]     # 接收人
  # 登录邮箱    账号 密码
  session.login([email protected], asdasdasdasdas)
  # 发送
  session.sendmail([email protected], [email protected], str(message))
  print(发送成功)
except:
  print(traceback.print_exc())  # 打印错误信息

 

 

发送html邮件:

#encoding=utf-8
# 导入模块
import smtplib
import email
from email.mime.text import MIMEText
import traceback
try:
  # 设置邮件的会话对象
  session = smtplib.SMTP_SSL(smtp.qq.com, 465, 30)
  # 设置邮件信息(邮件内容、邮件类型、标题、发送人、接收人)
  message = MIMEText(<h1>这是一封HTML邮件</h1>, html, utf-8)
  message[subject] = HTML邮件    # 标题
  message[from] = [email protected]   # 发送人
  message[to] = [email protected]     # 接收人
  # 登录邮箱    账号 密码
  session.login([email protected], asdasdasdasdas)
  # 发送
  session.sendmail([email protected], [email protected], str(message))
  print(发送成功)
except:
  print(traceback.print_exc())  # 打印错误信息

 

 

发送带附件的邮件:

#encoding=utf-8
# 导入模块
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import traceback
try:
  # 设置邮件的会话对象
  session = smtplib.SMTP_SSL(smtp.qq.com, 465, 30)
  # 设置邮件信息(邮件内容、邮件类型、标题、发送人、接收人)
  msg = MIMEMultipart()
  msg[subject] = 发送带附件的邮件
  msg[from] = [email protected]
  msg[to] = [email protected]

  # 追加邮件内容
  emailText = MIMEText(这是邮件的主体内容, pailn, utf-8)
  msg.attach(emailText)
  
  # 构建邮件附件
  f = open(1.png, mode="rb")
  attach1 = MIMEText(f.read(), base64, utf-8)
  f.close()
  attach1[Content-Type] = application/octet-stream
  attach1[Content-Disposition] = "attachment;filename=‘附件名字‘"
  msg.attach(attach1)
  
  # 登录邮箱    账号 密码
  session.login([email protected], asdasdasdasdas)
  # 发送
  session.sendmail([email protected], [email protected], str(msg))
  print(发送成功)
except:
  print(traceback.print_exc())  # 打印错误信息

 

 

 

发送邮件内容带本地图片的邮件:

#encoding=utf-8
# 导入模块
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
import traceback
try:
  # 设置邮件的会话对象
  session = smtplib.SMTP_SSL(smtp.qq.com, 465, 30)
  # 设置邮件信息(邮件内容、邮件类型、标题、发送人、接收人)
  msg = MIMEMultipart()
  msg[subject] = 内容带本地图片邮件  # 主题
  msg[from] = [email protected]
  msg[to] = [email protected]
  # 邮件内容
  htmlText = ‘‘‘
  <h1>邮件内容包含本地图片</h1>
  <img src = ‘cid:image1‘></img>
  ‘‘‘
  emailText = MIMEText(htmlText, html, utf-8)
  # 读取本地图片
  f = open(1.png, mode="rb")
  emailImg = MIMEImage(f.read())
  f.close()
  # 为读取的本地图片添加一个ID号,与内容嵌入的标签cid保持一致
  emailImg.add_header(Content-ID, image1)
  # 追加内容
  msg.attach(emailText)
  msg.attach(emailImg)
  # 登录邮箱    账号 密码
  session.login([email protected], asdasdasdasdas)
  # 发送
  session.sendmail([email protected], [email protected], str(msg))
  print(发送成功)
except:
  print(traceback.print_exc())  # 打印错误信息

 

 

Python 解析 XML文件

python 中使用 xml.dom.minidom模块来解析XML文件, xml.dom.minidom.parse() 用于打开一个XML文件,并将这个文件对象转为xmldom变量。

xmldom.documentElement 用于得到dom对象的根节点,并把获得的对象给root ,每一个节点都有它的 nodeName 、 nodeValue 、 nodeType 属性,  nodeName 为节点名字, nodeValue 是节点的值(只对文本节点有效), nodeType 是节点的类型, nodeType 的12种类型:

 

#encoding = utf-8
import xml.dom.minidom

# 打开一个XML文件
xmlcode = xml.dom.minidom.parse(dome.xml)
print(xmlcode)
# 获取XML文件的根节点
root = xmlcode.documentElement
# 获取节点的名字
print(root.nodeName)
print(root.nodeValue)
print(root.nodeType)
# 获得节点下的所有子节点  包括换行节点
print(root.childNodes)
# 获得根节点下的 stydent 子节点
stunodes = root.getElementsByTagName(student)
print(stunodes)
for n in stunodes:
  if stunodes.getAttribute(id) == 1:
    # 修改节点内容
    n.getElementsByTagName(name)[0].firstChild.data = 新内容
    break
# 判断属性是否存在
print(root.hasAttribute(id))
# 获取属性
print(root.getAttribute(id))
# 设置属性
root.setAttribute(id, 12)
# 创建一个新的节点 并添加到根节点下
newElemt = xmlcode.createElement(new)
newElemt.setAttribute(id, 13)
root.appendChild(newElemt)
# 把更改后的DOM对象写入文件
xmlFile = open(dome.xml, mode="w", encoding="utf-8")
xmlFile.write(xmlcode.toprettyxml())

 

在python 中使用 SAX 解析 XML

SAX是一种基于事件驱动的API , 通常包含三个事件: 开始解析标签事件、解析数据事件、结束解析标签事件

利用 SAX 解析 XML 文档涉及两个部分:解析器和事件处理器。解析器负责读取XML文档,并向事件处理器发送事件,如元素开始和元素结束事件。而事件处理器则负责对事件作出相应数据进行处理。

SAX解析XML适用场景:  1、对大型文件进行处理;  2、只需要文件的部分内容,或者只需从文件中得到特定信息。

#encoding=utf-8

#导入相关模块
import xml.sax
import xml.sax.handler

# 定义一个类,并继承 xml.sax.ContentHandler
class studentHander(xml.sax.ContentHandler):
  def __init__ (self):
    print(开始解析)

  #开始解析文档
  def startDocument(self):
    print(开始解析文档)

  #结束文档解析
  def endDocument(self):
    print(结束文档解析)

  # 开始解析某个标签
  def startElement(self, name, attrs):
    print({0}标签开始解析.format(name))
    if name == movie:
      print(该标签的属性值为{0}.format(attrs[title]))

  # 结束解析某个标签
  def endElement(self, name):
    print({0}标签结束解析.format(name))

  # 解析过程,获得解析得到的值
  def characters(self, content):
    print(解析过程,获得解析得到的值为{0}.format(content))

# 创建一个新的解析器对象并返回
parser = xml.sax.make_parser()

# 指定解析器的ContentHandler对象
parser.setContentHandler(studentHander())

# 解析XML文件
parser.parse("dome.xml")

 

多线程

运行中的程序,称为进程;一个程序对应一个进程,一个进程可包含多个线程。

线程,有时被称为轻量级进程,是程序执行流的最小单位。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

线程有就绪、阻塞、运行三种基本状态。

python 通过两个标准库 _thread 和 threading 提供对线程的支持。_thread 提供了低级别的、原始的线程以及一个简单的锁。相对于 threading 模块功能有限。

threading 模块包含了 _thread模块所有的方法,还提供其他的方法:

threading.currentThread() :  返回当前的线程变量

threading.enumerate():     返回一个包含正在运行的线程的list 。正在运行指线程启动后、结束前。不包括启动前和终止后的线程。

threading.activeCount() :  返回正在运行的线程数量,相对于 len(threading.enumerate())

除了threading模块的方法外,线程模块同样提供了 Thread 类来处理线程, Thread 类提供了以下方法:

run()  :           用来表示线程活动

start()  :         启动线程活动

join([time]):  等待至线程终止,这阻塞调用线程直至线程的join() 方法被调用终止,正常退出或者抛出未处理的异常或者是可选的超时发生

isAlive()  :      返回线程是否活动

getName()  :  返回线程名

setName() : 设置线程名

#encoding=utf-8
import threading
# 获得当前的线程对象
currentThread = threading.currentThread()
# 线程是否正在运行
print(currentThread.isAlive())
# 线程名字
print(currentThread.getName())
# 是否为后台线程
print(currentThread.isDaemon())
# 正在运行的线程数量
print(threading.activeCount())
# 正在运行的所有线程列表
print(threading.enumerate())

 

 

使用函数创建多线程:

#encoding=utf-8
import time
import _thread
def showTime(name, delayTime):
  count = 1
  while count <=6:
    time.sleep(delayTime)
    count = count + 1
    print(执行体的名字{0},当前时间{1}.format(name, time.ctime(time.time())))

# 依次执行各个方法,需要耗费长时间
# showTime(‘cs1‘, 2)
# showTime(‘cs2‘, 2)
# showTime(‘cs3‘, 2)
# showTime(‘cs4‘, 2)

# 使用函数创建多线程
try:
  # 开启设置多个线程
  _thread.start_new_thread(showTime, (cs1, 2))
  _thread.start_new_thread(showTime, (cs2, 2))
  _thread.start_new_thread(showTime, (cs3, 2))
  _thread.start_new_thread(showTime, (cs4, 2))
  print(设置完毕)
except:
  print(error)
finally:
  while True:
    pass

 

使用类来创建多线程:

#encoding=utf-8
import time
import threading
def showTime(name, delayTime):
  count = 1
  while count <=6:
    time.sleep(delayTime)
    count = count + 1
    print(执行体的名字{0},当前时间{1}.format(name, time.ctime(time.time())))

# 依次执行各个方法,需要耗费长时间
# showTime(‘cs1‘, 2)
# showTime(‘cs2‘, 2)
# showTime(‘cs3‘, 2)
# showTime(‘cs4‘, 2)

# 使用类来创建多线程
class myThread(threading.Thread):
  def __init__(self, name, delayTime):
    threading.Thread.__init__(self)    # 重点
    self.name = name
    self.delayTime = delayTime
  
  # 定义多线程执行逻辑,通过 start方法触发
  def run(self):
    print({0}多线程开始执行.format(self.name))
    showTime(self.name, self.delayTime)

myThread1 = myThread(cs1, 2)
myThread2 = myThread(cs2, 2)
myThread3 = myThread(cs3, 2)
myThread4 = myThread(cs4, 2)

myThread1.start()   # 调用 start 方法, 会自动调用类里面的 run 方法
myThread2.start()
myThread3.start()
myThread4.start()

 

多线程的同步:

#encoding=utf-8
import threading
import time

count = 2
# 获得 lock对象,锁,用于多线程的同步
lock = threading.Lock()

class saleTick(threading.Thread):
  def __init__(self, name):
    threading.Thread.__init__(self)
    self.name = name

  def run(self):
    global count, lock
    # 进行锁定,此时其他线程该部分进入阻塞状态,会等该部分解锁后继续执行,用于保持数据的同步
    lock.acquire()
    if count >= 1:
      time.sleep(2)
      count = count - 1
      print(买到票,余票{0}.format(count))
    else:
      print(没抢到票)
    # 进行解锁,其他线程可使用
    lock.release()



thread1 = saleTick(p1)
thread2 = saleTick(p2)
thread3 = saleTick(p3)
thread4 = saleTick(p4)

thread1.start()
thread2.start()
thread3.start()
thread4.start()

 

装饰器

装饰器是把一个函数作为参数的函数,常常用于扩展已有函数,即不改变当前函数状态下增加功能。 通常使用 @名称 标记

import time

# 定义一个装饰器
def countTime(f):
  def wrapper(*args, **kwargs):   # 传入参数,表示可有可无
    begin = time.time()
    f(*args, **kwargs)      # 这里代表的是使用装饰器的函数逻辑
    end = time.time()
    print(end - begin)
  return wrapper

@countTime
def test():
  for n in range(100):
    for t in range(100):
      pass


test()

 

 

 

 

 

 

单例模式

一个类自始至终只产生一个实例对象,称为单例模式。

单例模式三个要点:1、某个类只能有一个实例 ; 2、 它必须自行创建这个实例;  3、它必须自行向整个系统提供这个实例;

#encoding=utf-8

# 创建单例模式类
class Singleton(object):      # object 是所有类的父类
  # 定义一个变量存储实例对象
  _singletObject = None
  # 构造方法
  def __init__(self, *args, **kwargs):
    object.__init__(self, *args, **kwargs)
  # 初始化方法
  def __new__(cls, *args, **kwargs):
    if Singleton._singletObject is None:     # 如果当前没有实例对象
      Singleton._singletObject = object.__new__(cls)    #创建一个实例对象并存储
    return Singleton._singletObject               # 存在实例对象,直接将其返回
      

# 创建一个类,继承单例模式
class Myclass(Singleton):
  def __init__(self, name):
    Singleton.__init__(self)   # 调用父类的构造方法
    self.name = name

# 这里创建的都是同个实例对象
a = Myclass(a)
b= Myclass(b)

print(a.name)    # b
print(b.name)    # b

 

工厂模式

使用一个类根据传入参数的不同,依次去创建不同的类,称为工厂模式

#encoding=utf-8

# 父级共用类
class Person:
  sex = ‘‘
  name = ‘‘
  def __init__(self):
    pass
  def getName(self):
    return self.name
  def getSex(self):
    return self.sex

# 定义一个类
class Female(Person):
  def __init__(self, name, sex):
    self.name = name
    self.sex = sex

# 定义一个类
class Male(Person):
  def __init__(self, name, sex):
    self.name = name
    self.sex = sex


# 定义一个工厂模式的类
class PersonFactory:
  def get_person(self, name, sex):  # 定义一个方法,根据传入参数的不同,返回不同的实例对象
    if sex == F:
      return Female(name, sex)
    if sex == M:
      return Male(name, sex)

m1 = PersonFactory().get_person(小明, M)
print(m1.getName(), m1.getSex())   # 小明 M

m2 = PersonFactory().get_person(小花, F)
print(m2.getName(), m2.getSex())   # 小花 F

 

以上是关于Python进阶学习的主要内容,如果未能解决你的问题,请参考以下文章

python 机器学习有用的代码片段

我的OpenGL学习进阶之旅NDK开发中find_library查找的系统动态库在哪里?

我的OpenGL学习进阶之旅NDK开发中find_library查找的系统动态库在哪里?

学习笔记:python3,代码片段(2017)

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情

python进阶学习笔记