Python学习第三模块笔记(面向对象和简单socket编程)

1、面向对象编程

概念

Object Oriented Programming,OOP,利用类和对象创建各种模型来描述真实世界。OOP达到了软件工程的三个主要目标:重用性、灵活性和扩展性。

Class 类

对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象都具备的属性、共同的方法。类是多个函数的集合,一个模板。

Object 对象

一个类实例化后的实例,,一个类必须经过实例化后才能在程序中调用,一个类可以实例化多个对象,每个对象也可以有不同的属性。

三个特性
Encapsulation 封装

将类的数据和方法封装在类中,可以防止数据和方法被随意修改,使外部程序不用关注对象的内部构造,方便调用。类中封装了字段、方法,对象中封装了普通属性的值。

Inheritance 继承

可以使用现有类的所有功能,并在无需重新编写原来的类的情况夏对这些功能进行扩展;通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”;继承的过程即从一般到特殊的过程;实现继承可通过“继承”和“组合”;一般情况下,一个子类只能有一个基类,要实现多重继承可通过多级继承来实现;继承实现方式:1、实现继承,直接使用基类的属性和方法,无需额外编码 2、接口继承,仅使用属性和方法的名称,子类必须提供实现的能力(子类重构基类方法);使用继承时,两个类之间的关系应该是“属于”的关系。

Polymorphism 多态

“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现。多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。Python对于多态不直接支持,但可以间接实现。

类的语法
class Dog(object):    # Dog 类名(类名规范:开头大写)

    nature = "动物"    # 静态字段 保存在类中

    def __init__(self, name):    # 构造方法、初始化方法 包含生成一个角色时需要初始化的属性
        self.NAME = name    # 普通字段 在实例化时保存在对象中
        self.__age = 2    # 私有字段,外部无法访问

    def run(self):    # 类的方法(动态属性)
        print("%s正在欢快的跑。。。它的年龄是%s" % (self.NAME, self.__age))

    def age(self):    # 使用函数返回值的形式访问私有字段
        dog_age = self.__age
        return dog_age

d = Dog("二哈")    # d = Dog("二哈") == Dog(d, "二哈") d传递给self d为类实例化后的实例(对象),所以self即为实例本身 "二哈"赋值给__init__的name
d.run()    # d.run() == d.run(d)
print(d.nature)    # 直接访问静态字段
print(d.NAME)    # 可以直接访问普通字段
print(d.age())
print(d._Dog__age)    # 强制访问私有字段

私有字段:外部无法访问的属性,只能在内部调用。外部需要访问时可定义方法返回私有字段。

静态字段:所有属于该类的对象都可以访问的属性,在类中直接定义,每个对象中保存相同的东西时使用。

# 小技巧

class Dog(object):

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

d = Dog("二哈", 2)
print(d.__dict__)    # 以字典的形式打印变量

#结果
{'NAME': '二哈', 'AGE': 2}
类的析构方法
#用于对象不使用时回收

class Dog(object):

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

    def run(self):
        print("%s正在跑..." % self.NAME)

    def __del__(self):
        print("删除对象...")

d = Dog("二哈")
d.run()
del d    # 删除这个对象

#结果
二哈正在跑...
删除对象...
类的继承实例
class Dog(object):

    count = 0

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

    def run(self):
        print("%s在跑..." % self.NAME)

    def add_dog(self):
        Dog.count += 1
        print("多了一只狗:%s,一共有%s只狗。" % (self.NAME, Dog.count))


class Husky(Dog):

    def __init__(self, name, age, feature=""):
        super(Husky, self).__init__(name, age)    # 继承基类的属性
        self.Feature = feature
        Dog.add_dog(self)    # 继承基类的方法

    def run(self):    # 重构基类的方法
        print("%s在跑,它的特性%s" % (self.NAME, self.Feature))

d = Husky("二哈", "2")
d.run()

#结果
多了一只狗:二哈,一共有1只狗。
二哈在跑,它的特性傻
#多继承实例

class Animal(object):

    def animal_tall(self, tall):
        print("它的叫声%s" % tall)


class Dog(object):

    count = 0

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

    def run(self):
        print("%s在跑..." % self.NAME)

    def add_dog(self):
        Dog.count += 1
        print("多了一只狗:%s,一共有%s只狗。" % (self.NAME, Dog.count))


class Husky(Animal, Dog):

    def __init__(self, name, age, feature=""):
        super(Husky, self).__init__(name, age)    # 继承基类的属性
        self.Feature = feature
        Dog.add_dog(self)    # 继承Dog基类的方法
        Animal.animal_tall(self, "汪汪汪")    # 继承Animal基类的方法

    def run(self):    # 重构基类的方法
        print("%s在跑,它的特性%s" % (self.NAME, self.Feature))

d = Husky("二哈", "2")
d.run()

#结果
多了一只狗:二哈,一共有1只狗。
它的叫声汪汪汪
二哈在跑,它的特性傻
#类中的一个属性是一个对象,是一种什么有什么的关系
#继承中的组合实例

class F1(object):

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


class F2(object):

    def __init__(self, a):
        self.A = a


class F3(object):

    def __init__(self, b):
        self.B = b

d = F1("Python")    # d2.B.A.Name = "Python"
d1 = F2(d)    # d2.B.A = d
d2 = F3(d1)    # d2.B = d1
'''打印出Python'''
print(d2.B.A.Name)

#结果
Python
新式类vs经典类
# 新式类,主要使用

class Dog(object):

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


class Husky(Dog):

    def __init__(self, name, age, feature=""):
        super(Husky, self).__init__(name, age)
        self.Feature = feature
        print("%s的年龄是%s,它的特性是%s" % (name, age, self.Feature))

d = Husky("哈士奇", 2)

# 经典类

class Dog:

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


class Husky(Dog):

    def __init__(self, name, age, feature=""):
        Dog.__init__(self, name, age)
        self.Feature = feature
        print("%s的年龄是%s,它的特性是%s" % (name, age, self.Feature))

d = Husky("哈士奇", 2)

多继承中的查找方法

272315068126604

经典类,深度优先查找;新式类,广度优先查找(Python 2.x)Python 3.x中都是用广度优先查找

类的多态实例
class Animal(object):

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

    def talk(self):
        return "说话"


class Dog(Animal):

    def __init__(self, name):
        super(Dog, self).__init__(name)

    def talk(self):
        return "汪汪汪"


class Cat(Animal):

    def __init__(self, name):
        super(Cat, self).__init__(name)

    def talk(self):
        return "喵喵喵"


def animal_talk(obj):    # 使用函数实现多态“一个接口,多种实现”
    print(obj.talk())

d = Dog("狗狗")
c = Cat("猫猫")
animal_talk(d)    # 一个接口多种实现
animal_talk(c)
静态方法(@staticmethod)

保存在类中,调用时无需创建对象,可以有任意个参数,与类无关,相当于一个单纯的函数,通过类名调用。

#静态方法实例

class Dog(object):

    @staticmethod
    def dog_tall(tall):
        print("狗狗的叫声%s" % tall)

Dog.dog_tall("汪汪汪")    # 无需创建对象,直接调用

#结果
狗狗的叫声汪汪汪
类方法(@classmethod)

类方法只能访问公有属性,不能访问普通属性。

#访问普通属性

class Dog(object):
    dog_name = "Husky"

    def __init__(self, name):
        self.Name = name
        
    @classmethod
    def run(self):
        print("%s is run..." % self.Name)


d = Dog("哈士奇")
d.run()

#结果,报错
Traceback (most recent call last):
  File "F:/python/classmethod.py", line 16, in <module>
    d.run()
  File "F:/python/classmethod.py", line 12, in run
    print("%s is run..." % self.Name)
AttributeError: type object 'Dog' has no attribute 'Name'

#访问公有属性

class Dog(object):
    dog_name = "Husky"

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

    @classmethod
    def run(self):    #按照PEP8风格规范,self最好改成cls
        print("%s is run..." % self.dog_name)


d = Dog("哈士奇")
d.run()

#结果
Husky is run...
属性方法(@property)

把一个方法变成一个静态属性。

#静态属性已经不是方法了,调用时不能加括号‘

class Dog(object):
    def __init__(self, name):
        self.Name = name

    @property
    def run(self):
        print("%s is run..." % self.Name)


d = Dog("二哈")
d.run

#结果
二哈 is run...

#修改:@proerty.setter,需要写一个新方法
#删除:@proerty.delete,需要写一个新方法
类的特殊成员方法

__doc__:类的描述信息,e.g:

class Dog(object):
    """演示"""
    def __init__(self, name):
        self.Name = name


print(Dog.__doc__)

#结果
演示

__module__:当前操作的对象所在的模块

__class__:当前操作的对象的类

e.g:

#建两个文件

#aa.py

class Dog(object):
    def run(self):
        print("runrunrun")

#bb.py

import aa

d = aa.Dog()
print(d.__module__)
print(d.__class__)

#结果
aa
<class 'aa.Dog'>

__call__:使实例像函数一样被调用,e.g:

class Dog(object):
    def __init__(self, name):
        self.Name = name

    def __call__(self, *args, **kwargs):
        print("看看效果 %s" % self.Name)


d = Dog("二哈")
d()

#结果
看看效果 二哈

__dict__:查看类或对象中所有的成员(字典形式输出),e.g:

#对类和对象打印__dict__的区别

class Dog(object):
    def __init__(self, name, age):
        self.Name = name
        self.Age = age


print(Dog.__dict__)
d = Dog("二哈", 2)
print(d.__dict__)

#结果
{'__module__': '__main__', '__init__': <function Dog.__init__ at 0x000002345E53DE18>, '__dict__': <attribute '__dict__' of 'Dog' objects>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}
{'Name': '二哈', 'Age': 2}

__str__:若一个类中定义了__str__方法,打印对象时默认输出__str__方法的返回值,e.g:

class Dog(object):
    def __init__(self):
        pass

    def __str__(self):
        return "打印这里的内容"


d = Dog()
print(d)

#结果
打印这里的内容

__getitem__、__setitem__、__delitem__:用于索引操作,如字典。以上分别表示获取、设置、删除数据,e.g:

class Dog(object):
    def __getitem__(self, item):
        print("--getitem--", item)

    def __setitem__(self, key, value):
        print("--setitem--", key, value)

    def __delitem__(self, key):
        print("--delitem--", key)


d = Dog()
a = d["a1"]
d["a2"] = "a2"
del d["a1"]

#结果
--getitem-- a1
--setitem-- a2 a2
--delitem-- a1
类的起源与metaclass

Python中一切皆对象,所以类也是一个对象,所有的类都是type类的一个实例,type为类的类。

#可以通过以下方法来创建一个类

def run(self):
    print("%s is run..." % self.Name)


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


Dog = type("Dog", (object,), {"__init__": __init__, "run": run})
d = Dog("二哈")
d.run()

#结果
二哈 is run...

__new__:创建实例  __metaclass__:类由谁来实例化创建

下图为python 2.x创建类的过程

720333-20160901144705746-638616754

类的生成调用顺序依次是 __new__ --> __init__ --> __call__

反射

通过字符串映射或修改程序运行时的状态、属性、方法, 有4个方法:getattr、hasattr、setattr、delattr,e.g:

#hasattr:判断是否有字符串所对应的方法或属性

class Dog(object):
    def __init__(self, name):
        self.Name = name

    def run(self):
        print("%s is run..." % self.Name)


d = Dog("二哈")
print(hasattr(d, "Name"))
print(hasattr(d, "run"))
print(hasattr(d, "aaa"))

#结果
True
True
False
#getattr:获取字符串所对应的方法或属性

class Dog(object):
    def __init__(self, name):
        self.Name = name

    def run(self):
        print("%s is run..." % self.Name)


d = Dog("二哈")
print(getattr(d, "Name"))
print(getattr(d, "run"))

#结果
二哈
<bound method Dog.run of <__main__.Dog object at 0x000001FE3D41C908>>
#setattr:设置方法或属性

def talk(self, talk):
    print("%s talk %s" % (self.Name, talk))


class Dog(object):
    def __init__(self, name):
        self.Name = name

    def run(self):
        print("%s is run..." % self.Name)


d = Dog("二哈")
setattr(d, "Age", 2)    # 添加Age属性
print(d.Age)
setattr(d, "dog_talk", talk)    # 添加dog_talk方法,对应talk函数
print(d.dog_talk(d, "汪汪汪"))    # talk函数self也需要传值,把对象传入

#结果
2
二哈 talk 汪汪汪
None
#delattr:删除属性

class Dog(object):
    def __init__(self, name):
        self.Name = name

    def run(self):
        print("%s is run..." % self.Name)


d = Dog("二哈")
delattr(d, "Name")
print(d.Name)

#结果
Traceback (most recent call last):
  File "F:/python/反射.py", line 14, in <module>
    print(d.Name)
AttributeError: 'Dog' object has no attribute 'Name'
#name属性被删除后执行print就提示属性不存在

2、异常处理

在程序出错时捕抓错误,进行友好的提示而程序不会崩溃。基本语法如下:

try:
    with open("aa.text", "r") as f:    # 试图打开一个不存在的文件,将引发FileNotFoundError错误
        for i in f:
            print(i)
except FileNotFoundError as e:    # 捕抓FileNotFoundError错误
    print("出错了!请看错误提示:", e)

#结果
出错了!请看错误提示: [Errno 2] No such file or directory: 'aa.text'

异常处理的其他语法:

try:
    a = [1, 2]
    print(a[1])
    with open("aa.text", "r") as f:    # 试图打开一个不存在的文件,将引发FileNotFoundError错误
        for i in f:
            print(i)
except IndexError as e:    # 捕抓IndexError错误
    print("出错了!请看错误提示:", e)

#结果
Traceback (most recent call last):
2
  File "F:/python/异常处理.py", line 6, in <module>
    with open("aa.text", "r") as f:    # 试图打开一个不存在的文件,将引发FileNotFoundError错误
FileNotFoundError: [Errno 2] No such file or directory: 'aa.text'
#捕抓的是IndexError,但程序引发的是FileNotFoundError,所以无法捕抓

使用Exception捕抓任意异常(有些异常可能无法捕抓):

try:
    a = [1, 2]
    print(a[1])
    with open("aa.text", "r") as f:    # 试图打开一个不存在的文件,将引发FileNotFoundError错误
        for i in f:
            print(i)
except IndexError as e:    # 捕抓IndexError错误
    print("出错了!请看错误提示:", e)
except Exception as e:    # 捕抓任意异常
    print("出错了!请看错误提示:", e)

#结果
2
出错了!请看错误提示: [Errno 2] No such file or directory: 'aa.text'

异常处理中else的用法:

try:
    a = [1, 2]
    print(a[1])
except IndexError as e:    # 捕抓IndexError错误
    print("出错了!请看错误提示:", e)
except Exception as e:
    print("出错了!请看错误提示:", e)
else:    # 没有捕抓到错误时执行
    print("程序执行成功!")

#结果
2
程序执行成功!
finally,无论是否存在异常都要执行
try:
    a = [1, 2]
    print(a[1])
    with open("aa.text", "r") as f:
        pass
except IndexError as e:    # 捕抓IndexError错误
    print("出错了!请看错误提示:", e)
except Exception as e:
    print("出错了!请看错误提示:", e)
finally:    # 没有捕抓到错误时执行
    print("虽然出错了,但仍然会打印这句话!")

#结果
2
出错了!请看错误提示: [Errno 2] No such file or directory: 'aa.text'
虽然出错了,但仍然会打印这句话!

自定义异常

class Error(Exception):
    def __init__(self, msg):
        self.message = msg

    def __str__(self):
        return self.message


try:
    raise Error('我的异常')
except Error as e:
    print(e)

#结果
我的异常

常用异常

#AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x

#OError 输入/输出异常;基本上是无法打开文件

#ImportError 无法引入模块或包;基本上是路径问题或名称错误

#IndentationError 语法错误(的子类) ;代码没有正确对齐

#IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]

#KeyError 试图访问字典里不存在的键

#KeyboardInterrupt Ctrl+C被按下

#NameError 使用一个还未被赋予对象的变量

#SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)

#TypeError 传入对象类型与要求的不符合

#UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它

#ValueError 传入一个调用者不期望的值,即使值的类型是正确的

更多异常

ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError

断言

assert 1 == 2

#结果
Traceback (most recent call last):
  File "F:/python/异常处理.py", line 3, in <module>
    assert 1 == 2
AssertionError
#条件错误则抛出异常,用于出现问题时检查程序

动态导入模块

import importlib
importlib.import_module("字符串")
#使用字符串来导入一个模块

3、简单socket编程

socket,套接字,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同计算机之间的通信。应用程序通过socket向网络发送请求或应答请求。

socket编程基本步骤:

Client:定义地址簇及类型—>连接—>发送或接受数据—>连接关闭

Server:定义地址簇及类型—>绑定监听端口—>监听—>接受连接—>发送或接受数据—>连接关闭

socket编程实现

sk = socket.socket():定义地址簇及类型,有以下参数:
  • 地址簇:AF_INET,IPv4(默认);AF_INET6,IPv6;AF_UNIX,用于单一的Unix系统进程间通信
  • 类型:SOCK_STREAM,流式socket,TCP(默认);SOCK_DGRAM,数据报式socket ,UDP;SOCK_RAW,原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以,其次,SOCK_RAW也可以处理特殊的IPv4报文,此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头,SOCK_RAM通常仅限于高级用户或管理员运行的程序使用;SOCK_RDM,是一种可靠的UDP形式,即保证交付数据报但不保证顺序;SOCK_SEQPACKET 可靠的连续数据包服务
  • 协议:0,(默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议
sk.bind(address):s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址
sk.listen(backlog):开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量,backlog等于n,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为n。这个值不能无限大,因为要在内核中维护连接队列
sk.setblocking(bool):是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错
sk.accept():接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址,接收TCP 客户的连接(阻塞式)等待连接的到来
sk.connect(address):连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误
sk.connect_ex(address):同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
sk.close():关闭套接字
sk.recv(bufsize[,flag]):接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量,以字节计算,官方建议最大不超过8196。flag提供有关消息的其他信息,通常可以忽略
sk.recvfrom(bufsize[,flag]):与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址
sk.send(string[,flag]):将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送
sk.sendall(string[,flag]):将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常,内部通过递归调用send,将所有内容发送出去
sk.sendto(string[,flag],address):将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议
sk.settimeout(timeout):设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
sk.getpeername():返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
sk.getsockname():返回套接字自己的地址。通常是一个元组(ipaddr,port)
sk.fileno():套接字的文件描述符

简单socket编程实例

#server端

import socket

server = socket.socket()
server.bind(("127.0.0.1", 6699))
server.listen(5)
while True:
    print("正在等待连接...")
    conn, address = server.accept()
    print("%s正在连接服务端..." % address[0])
    while True:
        msg = conn.recv(1024000)
        print(msg)
        conn.sendall("成功接收信息...".encode("utf-8"))

server.close()
#client端

import socket

client = socket.socket()
client.connect(("127.0.0.1", 6699))
while True:
    msg = input("清输入发送的内容-->")
    client.sendall(msg.encode("utf-8"))
    msg_re = client.recv(1024000)
    print(msg_re.decode())

client.close()
原文地址:https://www.cnblogs.com/yu2006070/p/7757665.html