python学习笔记(二)

此文为学习廖雪峰的Python学习教程的笔记,接下来是更加高级的部分,将会进入到一些实际用途很大的章节,由于本人之前有一定的java编程经验,所以不自觉地会和java进行对比,如果没有学过请自行忽略,不影响阅读。如果有不恰当的地方,也欢迎进行纠正,以免误导他人。愿能有所收获,以下是正文部分:

面向对象编程

  • 区分面向对象与面向过程:面向过程是将计算机程序视为一系列命令的集合,即一组函数顺序执行。面向对象是将计算机程序视为一组对象的集合,对象包含数据和操作数据的方法。实际

操作时只用将类实例化为对象并调用函数进行操作即可,而且各对象之间可以互相影响,增强了代码的复用性和可维护性。

  • 类是抽象的模板,经过实例化才会成为一个个具体的对象。每个对象拥有的方法相同,但是数据可能不同。**由于Python是动态语言,因此可以在外部对对象添加属性,这个与java很不一

样。**

  • 创建类的格式:
    1. 通过class关键字定义类,之后紧跟类名,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的。如果没有明确的父类,就默认为object
    2. __init__方法可以将类需要绑定的属性写入。它的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的

实例本身。调用时不用写入self,但是要将必要参数写入。
3. 和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍

然可以用默认参数、可变参数和关键字参数。
4. 要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入
5. 封装有利于添加新的功能
6. 以下为示例:

class Test(object):

    def __init__(self,name,age):
        self.name = name
        self.age = age

    def print_msg(self):
        print 'name: %s age: %d' % (self.name,self.age)

steve = Test('steve' , 16)
steve.print_msg()

注意:在我敲这段代码时print_msg函数的参数忘加self导致错误,这是python类中的函数与一般函数不一样的地方,切记!

访问限制

java中私有变量可以通过private修饰符,公有变量可以通过public来声明,但是python中如果需要声明类的私有变量,就需要在变量前·加__修饰,当然和之前模块的私有变量一样,这个也是一个君子协定,仍然有方法调用私有变量,方法就是xxx,我们还是尽量做君子,不要使用这种方式为好,毕竟这样既不规范也不安全。
示例:

def __init__(self,name,age):
        self.__name = name
        self.__age = age

    def print_msg(self):
        print 'name: %s age: %d' % (self.__name,self.__age)

当需要访问和更改私有变量时,写访问器和更改器即可。例:

 def get_name(self):
    return self.__name
def set_age(self, age):
    self.__age = age

继承与多态

提到面向对象编程就肯定绕不开这两个特性,这是OOP三板斧中的两个(另一个就是之前提到的封装)。java中通过extend来表示继承关系,但是在python中继承很简单:

class Teacher(People):
...

括号里就是父类,括号外就是子类。前面也提到过,没有可继承的父类时,就用object填入。

多态表明的是子类同样也是父类,此例中指老师同时也是人,当一个方法需要People作为参数时,Teacher也可以作为参数传入,用java来描述就是父类的引用变量可以指向子类对象。

获取对象信息

type()函数
>>> type(123)
<type 'int'>
>>> type('str')
<type 'str'>
  • type()函数返回的是type类型,而所有的基本类型都属于type类型。
  • 如果一个变量指向函数或者类,也可以用type()判断:
>>> type(abs)
<type 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>

Python把每种type类型都定义好了常量,放在types模块里,使用之前,需要先导入:

>>> import types
>>> type('abc')==types.StringType
True
>>> type(u'abc')==types.UnicodeType
True
>>> type([])==types.ListType
True
>>> type(str)==types.TypeType
True
  • 有一种类型就叫TypeType,所有类型本身的类型就是TypeType,比如:
>>> type(int)==type(str)==types.TypeType
True
isinstance()函数
  • isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。
  • 能用type()判断的基本类型也可以用isinstance()判断:
>>> isinstance('a', str)
True
>>> isinstance(u'a', unicode)
True
>>> isinstance('a', unicode)
False
  • 还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是str或者unicode:
>>> isinstance('a', (str, unicode))
True
>>> isinstance(u'a', (str, unicode))
True

由于str和unicode都是从basestring继承下来的,所以,还可以把上面的代码简化为:

>>> isinstance(u'a', basestring)
True
dir()函数

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
  • 操作对象的属性:getattr()setattr()以及hasattr(),分别可用于获取属性,设置属性和查询属性是否存在。具体实例参考获取对象信息

面向对象高级编程

使用__slots__

由于python是一门动态语言,允许在调用的过程中添加属性,但如果给Student添加不被允许的方法或属性就会导致一些不必要的麻烦,因此这时需要__slots__属性来限制类的属性添加,例如:

>>> class Student(object):
...     __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
...

此时如果动态添加除name和age外的属性,程序就会报错,但是这条规则对子类无效,因为子类的__slots__包含的内容除了父类的还有自己的。

@property

一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,例如:

class Test(object):

    @property
    def name(self):
        return self._name
    @name.setter
    def name(self,val):
        self._name = val

    @property
    def age(self):
        return self._age

多重继承

如果一个类具有多个父类的属性,可以通过多继承来实现。多重继承的实现只需要直接在括号中添加其他父类即可:

class Student(People,PlayGame)
  • Mixin
    由于继承关系主线一般都是单链,例如学生继承自人,但是玩游戏也是需要额外添加的功能。因此为了更好地确立关系,需要引入Mixin,这是一种用于明确表明额外功能而非父类(此例中就是学生的父类为人,但是可以加入玩游戏的功能),此时需要进行一点改进:
class Student(People,PlayGameMixin)

定制类

  • __str__()
    当直接print对象时,就会调用__str__方法,对其进行重定义即可获得自己定制的字符串。

  • __repr__()
    返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。当在交互界面直接输入对象的引用时就会调用此方法。

  • __iter__()
    如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def next(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration();
        return self.a # 返回下一个值

调用实例作用于for循环:

>>> for n in Fib():
...     print n
...
1
1
2
3
5
...
46368
  • __getitem__()
    用于使引用变量可以像list那样取每一个值。例如:
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

利用类似下标访问:

>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3

– 切片对象slice
加上slice对象判断,可以对引用变量使用切片:

class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice):
            start = n.start
            stop = n.stop
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  • __getattr__()
    当本类中没有相关属性时,会调用此方法查找,因此它可以用于存放额外变量。

  • __call__()
    本方法可以用于将实例直接调用,而不只是通过调用方法来操作数据。

  • callable()函数
    通过此函数可判断一个对象是否可调用,如:

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('string')
False

错误处理及调试

错误处理

  • try...except...finally可用于异常处理,当执行出错时执行except后面的代码,except后可添加else,当没有异常时执行。finally后的代码是无论有无异常均会执行,这与java的try…catch…finally相似。

  • raise可以将异常抛出,与java中throws相似。不过它可以更改抛出的异常类型,这点比较特殊。

调试

  1. print 这个就是在你需要知道某个变量值的地方打印一下就行,简单粗暴,不利于后期清除。
  2. assert 断言,这个相当于设了断点,错误时抛出AssertionError,内容就是你的第二个字符串参数。如:assert n != 0, 'n is zero!'
  3. logging 它允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
  4. pdb 通过python -m pdb file_name.py启动(file_name是你的python文件名,此处只是在正常执行语句中间加了-m pdb)常用操作有:

    • l 列出代码

    • p 变量名 查看变量值

    • n 执行下一行代码

    • q 结束调试,退出程序

  5. pdb.set_trace() 设置断点:

import pdb

s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print 10 / n

运行代码,程序会自动在pdb.set_trace()暂停并进入pdb调试环境,可以用命令p查看变量,或者用命令c继续运行.关于pdb的一些命令

IO编程

  1. 使用内置的open()函数,通过read()可以一次读取所有内容,通过write()可以写入文件。
>>> f = open('e:/test.txt', 'r')
>>> f.read()
'Hello, world!'
>>> f.write('test')
>>> f.close()

2.用with... as...来简化代码,可以免去每次打开完文件后关闭文件,自动用完后调用close():

with open('/path/to/file', 'r') as f:
    print f.read()

3.codecs可用于自动转换成相应编码:

import codecs
with codecs.open('e:/gbk.txt', 'r', 'gbk') as f:
    f.read()

4.read(size)方法,每次最多读取size个字节的内容。类似于java中的buffer.直接调用read()会读取全部内容。
5.调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。
6.file-like Object :像open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。file-like Object不要求从特定类继承,只要写个read()方法就行。

序列化

  • 我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

  • Python提供两个模块来实现序列化:cPicklepickle。这两个模块功能是一样的,区别在于cPickle是C语言写的,速度快,pickle是纯Python写的,速度慢,跟cStringIOStringIO一个道理。用的时候,先尝试导入cPickle,如果失败,再导入pickle:

try:
    import cPickle as pickle
except ImportError:
    import pickle
  • pickle.dumps()方法把任意对象序列化成一个str,然后,就可以把这个str写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object:
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
"(dp0\nS'age'\np1\nI20\nsS'score'\np2\nI88\nsS'name'\np3\nS'Bob'\np4\ns."

>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
  • 如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。
  • 把Python对象变成一个JSON:
>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'

dumps()方法返回一个str,内容就是标准的JSON。

  • 反序列化得到的所有字符串对象默认都是unicode而不是str。由于JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在Python的str或unicode与JSON的字符串之间转换。

多进程与多线程

多进程

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

  • multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:
from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__=='__main__':
    print 'Parent process %s.' % os.getpid()
    p = Process(target=run_proc, args=('test',))
    print 'Process will start.'
    p.start()
    p.join()
    print 'Process end.'

执行结果如下:

Parent process 17896.
Process will start.
Run child process test (16660)...
Process end
  • 创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动。
    join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
Pool

如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

from multiprocessing import Pool
import os , time, random

def long_time_task(name):
    print 'Run task %s (%s) ...' % (name, os.getpid())
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print 'Task %s runs %0.2f seconds. ' % (name, (end - start))

if __name__ == '__main__' :
    print 'Parent process %s.' % os.getpid()
    p = Pool()
    for i in range(5):
        p.apply_async(long_time_task, args=(i,))
    print 'Waiting for all subprocesses done ...'
    p.close()
    p.join()
    print 'All subprocess done.'

结果如下:

Parent process 24112.
Waiting for all subprocesses done ...
RRRun task 1 (43460) ...un task 0 (1032) ...
Run task 2 (27968) ...
un task 3 (52252) ...

Task 0 runs 0.25 seconds.
Run task 4 (1032) ...
Task 1 runs 0.81 seconds.
Task 2 runs 1.65 seconds.
Task 4 runs 2.17 seconds.
Task 3 runs 2.46 seconds.
All subprocess done.

注意:pool默认大小为电脑的核数,如果要改为默认一次执行10个,则可写成p=Pool(10)

进程间通信
  • multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
from multiprocessing import Process, Queue
import os , time, random

def write(q):
    for value in ['A' , 'B' , 'C']:
        print 'Put %s to queue...' % value
        q.put(value)
        time.sleep(random.random())

def read(q):
    while True:
        value = q.get(True)
        print 'Get %s from queue.' % value

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args = (q,))
    pr = Process(target=read, args = (q,))
    pw.start()
    pr.start()
    pw.join()
    pr.terminate()

结果如下:

from multiprocessing import Process, Queue
import os , time, random

def write(q):
    for value in ['A' , 'B' , 'C']:
        print 'Put %s to queue...' % value
        q.put(value)
        time.sleep(random.random())

def read(q):
    while True:
        value = q.get(True)
        print 'Get %s from queue.' % value

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args = (q,))
    pr = Process(target=read, args = (q,))
    pw.start()
    pr.start()
    pw.join() #等待pw结束
    pr.terminate() #pr是死循环,必须强行终止

多线程

Python的标准库提供了两个模块:threadthreading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

  • 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
import time, threading

def loop():
    print 'thread %s is running...' % threading.current_thread().name
    n = 0
    while n < 5:
        n = n + 1
        print 'thread %s >>> %s' % (threading.current_thread().name, n)
        time.sleep(1)
    print 'thread %s ended' % threading.current_thread().name

print 'thread %s is running...' %  threading.current_thread().name
t = threading.Thread(target = loop, name = 'LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name

结果如下:

thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended
thread MainThread ended.

由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1,Thread-2……

Lock

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
用法示例:

balance = 0
lock = threading.Lock()

def run_thread(n):
    for i in range(100000):
        # 先要获取锁:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要释放锁:
            lock.release()

当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
注:此处try...finally用于保证即使有异常也要释放锁,防止其他线程持续等待。

因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

python多线程无法利用多核的优势,就是由于GIL的存在,不过也不用担心,利用多进程可以避免这种问题。

ThreadLocal

为了解决多线程之间数据不方便共享,创建局部变量层层传递又太麻烦的问题,引入了ThreadLocal:

import threading

# 创建全局ThreadLocal对象:
local_school = threading.local()

def process_student():
    print 'Hello, %s (in %s)' % (local_school.student, threading.current_thread().name)

def process_thread(name):
    # 绑定ThreadLocal的student:
    local_school.student = name
    process_student()

t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()

结果如下:

Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)

全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。

网络编程

TCP编程

客户端:

# -*- coding: utf-8 -*-

# 导入socket
import socket
import codecs
# 创建一个socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立连接
s.connect(('www.baidu.com', 80))

# 发送数据
s.send('GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n')
# 接收数据
buffer = []
while True:
    #每次最多接受1K字节

    d = s.recv(1024).decode('utf-8')
    if d:
        buffer.append(d)
    else:
        break

data = ''.join(buffer)

#关闭连接
s.close()

header, html = data.split('\r\n\r\n', 1)
print header
# 把接收的数据写入文件
with codecs.open('baidu.html', 'w' , 'utf-8') as f:
    f.write(html)
  • 创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议,这样,一个Socket对象就创建成功,但是还没有建立连接。

  • 每种小于1024的端口都有固定作用。80用于网络服务。

  • connect()的参数是一个元组,包括服务器IP和端口号。

  • 接收数据时,调用recv(max)方法,一次最多接收指定的字节数,因此,在一个while循环中反复接收,直到recv()返回空数据,表示接收完毕,退出循环。

当我们接收完数据后,调用close()方法关闭Socket,这样,一次完整的网络通信就结束了。

服务端:
服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。

所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。

但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。

import socket,threading,time

# 创建一个socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 监听端口
s.bind(('127.0.0.1' , 9999))

s.listen(5)
print 'Waiting fro connection...'

def tcplink(sock, addr):
    print 'Accept new connection from %s:%s...' % addr
    sock.send('Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if data == 'exit' or not data:
            break
        sock.send('Hello, %s!' % data)
    sock.close()
    print 'Connection from %s:%s closed.' % addr

while True:
    # 接受一个新连接:
    sock , addr = s.accept()
    # 创建新线程处理TCP连接:
    t = threading.Thread(target=tcplink, args = (sock, addr))
    t.start()

UDP编程

UDP的使用与TCP类似,但是不需要建立连接。此外,服务器绑定UDP端口和TCP端口互不冲突,也就是说,UDP的9999端口与TCP的9999端口可以各自绑定。

服务器端:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口:
s.bind(('127.0.0.1', 9999))
print 'Bind UDP on 9999...'
while True:
    # 接收数据:
    data, addr = s.recvfrom(1024)
    print 'Received from %s:%s.' % addr
    s.sendto('Hello, %s!' % data, addr)

recvfrom()方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端。SOCK_DGRAM代表用UDP方式传输。

客户端:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in ['Michael', 'Tracy', 'Sarah']:
    # 发送数据:
    s.sendto(data, ('127.0.0.1', 9999))
    # 接收数据:
    print s.recv(1024)
s.close()

此处通过sendto发送,recv接收.


以上是关于python面向对象,IO,多线程,异常处理,网络编程的基础内容,其他内容后面有机会再补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值