类的几个重要方法
静态方法(staticmethod)、类方法(classmethod)、属性方法(property)
静态方法
定义:在方法名前加上@staticmethod装饰器,表示此方法为静态方法,只是名义上归类管理,实际上在静态方法里访问不了类或者实例的任何属性。
class Dog(object):
def __init__(self, name):
self.name = name
@staticmethod # 在方法前加上staticmethod 装饰器定义静态方法
def eat():
print("dog is eating")
特性:
静态方法,不可以传入self参数,但是想传也可以,调用时必须传入实例本身
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Dog(object):
def __init__(self, name):
self.name = name
@staticmethod # 定义静态方法
def eat(self, food): # 可以定义,但是需传入实例本身
print("%s is eating %s"%(self.name, food))
d = Dog("wu")
d.eat(d,‘baozi‘) # 传入实例d本身,否则会报错
#输出
wu is eating baozi
静态方法可以用类直接调用,直接调用时,不可以直接传入self,否则会报错
class Dog(object):
def __init__(self, name):
self.name = name
@staticmethod # 定义静态方法
def eat(food): # 可以定义,但是需传入实例本身
print("is eating %s"%food)
Dog.eat(‘baozi‘)
#输出
is eating baozi
类方法
定义:
在方法名前加上@classmethod装饰器,表示此方法为类方法,只能访问类变量,不能访问实例变量。
class Dog(object):
name = ‘alex‘#定义静态属性
def __init__(self, name):
self.name = name
@classmethod#定义类方法
def eat(self, food):
print("%s is eating %s"%(self.name, food))
d = Dog("wu")
特性
只能访问类变量(又叫静态属性),不能访问实例变量
1、访问实例变量
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Dog(object):
#name = ‘alex‘#定义静态属性
def __init__(self, name):
self.name = name
@classmethod#定义类方法
def eat(self, food):
print("%s is eating %s"%(self.name, food))
d = Dog("wu")
d.eat(‘baozi‘)
#输出
Traceback (most recent call last):
File "D:/[4]python/untitled/学习/day7/类方法.py", line 16, in <module>
d.eat(‘baozi‘)
File "D:/[4]python/untitled/学习/day7/类方法.py", line 12, in eat
print("%s is eating %s"%(self.name, food))
AttributeError: type object ‘Dog‘ has no attribute ‘name‘
2、访问类变量
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Dog(object):
name = ‘alex‘#定义静态属性
def __init__(self, name):
self.name = name
@classmethod#定义类方法
def eat(self, food):
print("%s is eating %s"%(self.name, food))
d = Dog("wu")
d.eat(‘baozi‘)
#输出
alex is eating baozi
使用场景
一个国家,有的国家不允许更改国籍,比如朝鲜,只能去访问写死的变量
属性方法
定义:
在方法名前加上@property装饰器,表示此方法为属性方法
class Dog(object):
def __init__(self, name):
self.name = name
@property #定义属性方法
def eat(self, food):
print("%s is eating %s"%(self.name, food))
特性:
把一个方法变成一个静态属性
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Dog(object):
def __init__(self, name):
self.name = name
@property #定义属性方法
def eat(self):
print("%s is eating"%(self.name))
d = Dog("wu")
d.eat#把方法变成静态属性调用
#输出
wu is eating
传入参数
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Dog(object):
def __init__(self, name):
self.name = name
self.__food = None
@property #定义属性方法
def eat(self):
print("%s is eating %s"%(self.name,self.__food))
@eat.setter#定义可以设置变量赋值
def eat(self,food):
print(‘set to food:‘,food)
self.__food = food
d = Dog("wu")
d.eat#第一份赋值的是None
d.eat = ‘baozi‘
d.eat #自定义后的
#输出
wu is eating None
set to food: baozi
wu is eating baozi
删除删除转化后的静态属性
class Dog(object):
def __init__(self, name):
self.name = name
self.__food = None
@property #定义属性方法
def eat(self):
print("%s is eating %s"%(self.name,self.__food))
@eat.setter#定义可以设置变量赋值
def eat(self,food):
print(‘set to food:‘,food)
self.__food = food
@eat.deleter
def eat(self):
del self.__food
print(‘food删除完毕‘)
d = Dog("wu")
del d.eat
#输出
food删除完毕
使用场景
你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步
- 连接航空公司API查询
- 对查询结果进行解析
- 返回结果给你的用户
因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Flight(object):
def __init__(self, name):
self.flight_name = name
def checking_status(self):
print("checking flight %s status " % self.flight_name)
return 1
@property
def flight_status(self):
status = self.checking_status()
if status == 0:
print("flight got canceled…")
elif status == 1:
print("flight is arrived…")
elif status == 2:
print("flight has departured already…")
else:
print("cannot confirm the flight status…,please check later")
@flight_status.setter # 修改
def flight_status(self, status):
status_dic = {
0: "canceled",
1: "arrived",
2: "departured"
}
print("\033[31;1mHas changed the flight status to \033[0m", status_dic.get(status))
@flight_status.deleter # 删除
def flight_status(self):
print("status got removed…")
f = Flight("CA980")
f.flight_status
f.flight_status = 2 # 触发@flight_status.setter
del f.flight_status # 触发@flight_status.deleter
#输出
checking flight CA980 status
flight is arrived…
Has changed the flight status to departured
status got removed…
类的特殊成员方法
doc:表示类的描述信息
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Dog(object):
"""此类是形容Dog这个类""" # 类的描述信息
def __init__(self, name):
self.name = name
print(Dog.__doc__) # 打印类的描述信息
#输出
此类是形容Dog这个类
module: 表示当前操作的对象在哪个模块
class:表示当前操作的对象的类是什么
aa:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class C(object):
def __init__(self):
self.name = "shuaigaogao"
index
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from lib.aa import C
obj = C()
print(obj.__module__) # 表示当前操作的对象在哪个模块
print(obj.__class__) # 表示当前操作的对象的类是什么
#输出
lib.aa
<class ‘lib.aa.C‘>
__init__构造方法,通过类创建对象时,自动触发执行
_del__:析构方法,当对象在内存中被释放时,自动触发执行
call 对象后面加括号,触发执行
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class foo(object):
def __init__(self):
self.name = ‘li‘
def __call__(self, *args, **kwargs):#重写call方法
print(‘running call‘,args,kwargs)
f = foo() #执行init
f(1,2,3,name=4) # 执行call方法,也可以写成 Foo()(1,2,3,name=4)
#输出
running call (1, 2, 3) {‘name‘: 4}
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
dict 查看类或对象中的所有成员
打印类中所有的属性,不包括实例属性:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Province(object):
country = ‘China‘
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print("func")
print(Province.__dict__) # 类.__dict__
#输出
{‘__module__‘: ‘__main__‘, ‘country‘: ‘China‘, ‘__init__‘: <function Province.__init__ at 0x03503540>, ‘func‘: <function Province.func at 0x035034F8>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Province‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Province‘ objects>, ‘__doc__‘: None}
打印实例的所有属性,不包括类属性
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Province(object):
country = ‘China‘
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print("func")
p = Province("jiangsu",20000) #实例化
print(p.__dict__) #实例名.__dict__
#输出
{‘name‘: ‘jiangsu‘, ‘count‘: 20000}
str:如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Province(object):
country = ‘China‘
def __init__(self, name):
self.name = name
def __str__(self):
return "<obj:{0}>".format(self.name)
p = Province("lisir")
print(p)
#输出
<obj:lisir>
getitem、setitem、__delitem__用于索引操作,如字典。以上分别表示获取、设置、删除数据
class Foo(object):
def __getitem__(self, key):
print(‘__getitem__:‘, key)
def __setitem__(self, key, value):
print(‘__setitem__:‘, key, value)
def __delitem__(self, key):
print(‘__delitem__‘, key)
f = Foo()
f["name"] = "lisir" # 自动触发__setitem__方法
f["name"] # 自动触发__getitem__方法
del f["name"] # 自动触发__delitem__方法
#输出
__setitem__: name lisir
__getitem__: name
__delitem__ name
番外篇:类的起源
创建传统类:
class Foo(object):
def __init__(self, name):
self.name = name
f = Foo("lisir")
print(type(f))
print(type(Foo))
#输出
<class ‘__main__.Foo‘>
<class ‘type‘>
f 是通过 Foo 类实例化的对象,其实,不仅 f 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象,按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。f对象是Foo类的一个实例,Foo类对象是 type 类的一个实例。
type创建类
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def func(self):
print(‘hi %s‘%self.name)
def __init__(self,name):
self.name = name
#通过type创建类,如果是经典类的话则写成:Foo = type("Foo",(),{"talk":func,"__init__":__init__})
Foo = type("Foo",(object,),{"talk":func,"__init__":__init__})
f = Foo(‘li‘)
f.talk()
#输出
hi li
new方法作用
作用:所有对象都是通过new方法来实例化的,new里面调用了init方法,所以在实例化的过程中先执行的是new方法,而不是init方法。
重构__new__方法
class Foo(object):
def __init__(self, name):
self.name = name
print("Foo __init__")
def __new__(cls, *args, **kwargs):#cls相当于传入类Foo
print("Foo __new__", cls, *args, **kwargs)
return object.__new__(cls) # 继承父类的__new__方法,这边必须以返回值的形式继承
f = Foo(‘li‘)
#输出
Foo __new__ <class ‘__main__.Foo‘> li
Foo __init__
大多数情况下,你都不要去重构你的__new__方法,因为你父类中已经有__new__方法了,已经帮你写好了怎么去创建类,如果你重写的话,就会覆盖父类的里面的__new__方法。但是你重构可以增加一点小功能,但是你覆盖了以后还是需要继承父类回来,要不然你的这个实例就创建不了。
使用场景
对自己写的一些类进行定制,就在它实例化之前就进行定制,就可以用到__new__方法,new方法就是用来创建实力的,重构new方法,必须以返回值的形式继承父类的new方法。
比较重要的知识:反射
反射函数:
hasarttr(obj,name_str):判断一个对象obj中是否有对应的name_str字符串的属性或者方法
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Dog(object):
def __init__(self, name):
self.name = name
def eat(self, food):
print(‘%s is eat %s‘%(self.name,food))
d = Dog(‘li‘)
choose = input(‘>>>:‘).strip()
print(hasattr(d,choose)) #obj中是否有对应的choice字符串的属性或者方法
#输出
>>>:eat
True
getattr(obj,name_str):根据字符串name_str获取obj对象中的对应方法的内存地址或者对应属性的值
class Dog(object):
def __init__(self, name):
self.name = name
def eat(self, food):
print(‘%s is eat %s‘%(self.name,food))
d = Dog(‘li‘)
choose = input(‘>>>:‘).strip()
#print(hasattr(d,choose)) #obj中是否有对应的choice字符串的属性或者方法
print(getattr(d,choose))#获取obj对象中的对应方法的内存地址或者对应属性的值
choose = input(‘>>>:‘).strip()
print(getattr(d,choose))
#输出
>>>:name
li
>>>:eat
<bound method Dog.eat of <__main__.Dog object at 0x030811B0>>
setattr(x,y,z):给obj对象添加一个新属性或者新方法,setattr(x, ‘y‘, v) is equivalent to ``x.y = v‘‘
给对象新增一个新方法
def bulk(self):
print(‘%s is yelling‘%self.name)
class Dog(object):
def __init__(self,name):
self.name = name
def eat(self,food):
print(‘%s is eat %s‘%(self.name,food))
d = Dog(‘li‘)
choose = input(‘>>>:‘).strip()
setattr(d,choose,bulk) #输入的是talk,所以又等同于d.talk = bulk
#d.talk(d) 直接写死,用d.talk(d),一般不这么写
func = getattr(d,choose)#用getattr来获取
func(d)
#输出
>>>:talk
li is yelling
给对象新增一个属性
class Dog(object):
def __init__(self,name):
self.name = name
def eat(self,food):
print(‘%s is eat %s‘%(self.name,food))
d = Dog(‘li‘)
choose = input(‘>>>:‘).strip()
setattr(d,choose,22) #输入的是age,所以等于d.age = 22
#print(d.age) 道理一样,写死
print(getattr(d,choose))
#输出
>>>:age
22
delattr(x,y):删除obj对象中的属性或者方法,delattr(x, ‘y‘) is equivalent to ``del x.y‘‘
class Dog(object):
def __init__(self, name):
self.name = name
def eat(self, food):
print("%s is eating %s"%(self.name, food))
d = Dog("li")
choice = input(">>>:").strip()
delattr(d, choice) # 根据字符串删除属性或者方法
print(d.name)
print(d.eat)
#输出
>>>:eat #删除方法
Traceback (most recent call last):
File "D:/[4]python/untitled/学习/day7/反射/delattr.py", line 15, in <module>
delattr(d, choice) # 根据字符串删除属性或者方法
AttributeError: eat
反射综合使用:
class Dog (object):
def __init__(self,name):
self.name = name
def eat(self,food):
print(‘%s is eat %s‘%(self.name,food))
d = Dog(‘li‘)
choose = input(‘>>>:‘).strip()
if hasattr(d,choose):#判断d对象中存在属性和方法
name_value = getattr(d,choose)#获取属性值
print(name_value)
setattr(d,choose,‘ok‘)#修改属性值
print(getattr(d,choose))
else:
setattr(d,choose,None)#设置不存在的属性值为None
e = getattr(d,choose)
print(e)
#输出:
>>>:name
li
ok
>>>:eat
<bound method Dog.eat of <__main__.Dog object at 0x02E011B0>>
ok
>>>:talk
None
异常处理
异常处理是当程序出错了,但是我们又不想让用户看到这个错误,在写程序的时候已经预料到了它可以出现这样的错误,出现这样的错误代表着什么,我们可以提前处理这些个错误
处理单个异常:
name = [1,2,3]
try:
name[3]#列表中没有第四号元素(0,1,2,3)
except IndexError as e :#抓取 IndexError 这个异常
print(e)#e是错误的详细信息
#输出
list index out of range
处理多个异常:
情况1:
name = [1,2,3]
data = {"a":"b"}
try:
data["c"] #这边已经出现异常KeyError ,所以直接跳出code,跳到KeyError 下去处理
name[3]
except IndexError as e:
print(e)
except KeyError as e:
print(e)
#输出
‘c‘
情况2:
Exception异常
抓住所有异常,一般不用。
try:
open("qigao.text","r",encoding="utf-8")
except (IndexError,KeyError) as e: #没有IndexError,KeyError这两个异常
print(e)
except Exception as e: #只能通过这个异常处理,Exception 抓住所有的异常
print(e)
#输出
[Errno 2] No such file or directory: ‘qigao.text‘
else:没有异常,则走else部分的逻辑代码
try:
print("li,handson") #代码没有异常
except (IndexError,KeyError) as e:
print(e)
except Exception as e:
print(e)
else: #没有异常出错,走else的逻辑代码
print("没有异常")
#输出
li,handson
没有异常
finnally:不管有没有错误,都会执行finnally中的代码
try:
print("ok,everybody") #没有异常
except (IndexError,KeyError) as e:
print(e)
except Exception as e:
print(e)
else:
print("没有异常")
finally:
print("不管有没有错,haha")
#输出
ok,everybody
没有异常
不管有没有错,haha
2
try:
data = {"a":"b"}
data["c"] #data字典中没有‘c‘这个key值
except (IndexError,KeyError) as e:
print(e)
except Exception as e:
print(e)
else:
print("没有异常")
finally:
print("不管有没有错,ok")
#输出
‘c‘
不管有没有错,ok
常见异常
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x |
IOError 输入/输出异常;基本上是无法打开文件 |
ImportError 无法引入模块或包;基本上是路径问题或名称错误 |
IndentationError 语法错误(的子类) ;代码没有正确对齐 |
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] |
KeyError 试图访问字典里不存在的键 |
KeyboardInterrupt Ctrl+C被按下 |
NameError 使用一个还未被赋予对象的变量 |
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) |
TypeError 传入对象类型与要求的不符合 |
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它 |
ValueError 传入一个调用者不期望的值,即使值的类型是正确的 |
自定义异常
class liError(Exception): # 定义一个异常类,继承Exception
def __init__(self, message):
self.message = message
def __str__(self):
return self.message # 给对象取一个名字
try:
raise liError("数据库连接不上了") #触发自定义异常
except liError as e:
print(e)
#输出
数据库连接不上了
socket通信
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
网络通信图:
代码流程图:
socket概念
Socket Families(地址簇)
1、socket.AF_UNIX unix本机进程间通信
2、socket.AF_INET IPV4
3、socket.AF_INET6 IPV6
Socket Types
1、socket.SOCK_STREAM #for tcp
2、socket.SOCK_DGRAM #for udp
3、socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
4、socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
5、socket.SOCK_SEQPACKET #废弃了
socket实例
客户端:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
client = socket.socket() #创建socket实例
client.connect((‘localhost‘,11111)) #建立连接
send_data = ‘hello word‘#发送的字符串
send_data = send_data.encode()#转换成bytes类型
client.send(send_data)#开始发送数据
data = client.recv(1024)#接收服务器的数据,设置接受缓存大小是102400k
print(‘发送的‘,data.decode())
client.close()#关闭连接
服务端:
import socket
server = socket.socket()#创建服务端实例
server.bind((‘localhost‘,11111))#绑定客户端ip和端口
server.listen()#监听端口
print(‘开始接电话‘)
conn,addr = server.accept()#接受客户端,并且返回连接标志位(conn)实例,和对方的地址(addr)
data = conn.recv(1024)#接收客户端的数据
print(‘我的电话来了‘)
print(‘接受的‘,data.decode())
conn.send(data.upper())#发送数据至服务端
server.close()
运行结果:
#客户端
发送的 HELLO WORD
#服务端
开始接电话
我的电话来了
接受的 hello word
注:因为在python 3中只能接受bytes类型的数据,bytes类型只能接受ASCII码里面的数据类型。因为bytes类型是一个ASCII 0-255的数字组合。所以在客户端向服务端传中文时一定要先转成bytes类型,也就是encode(),接收方需要解码,也就是decode()才能识别中文。
重复发送和多次接收
客户端:
import socket
client = socket.socket()
client.connect((‘localhost‘,11111))
while True:
msg = input(‘>>>:‘)
client.send(msg.encode())
data = client.recv(1024)
print(data)
client.close()
服务端
import socket
server = socket.socket()
server.bind((‘localhost‘,11111))
server.listen()
conn,addr = server.accept()
print(‘电话来了‘)
while True:
data = conn.recv(1024)
print(data)
conn.send(data)
server.close()
运行结果:
#client
>>>:1
b‘1‘
>>>:2
b‘2‘
>>>:3
b‘3‘
>>>:4
b‘4‘
>>>:as
b‘as‘
>>>:好
b‘\xe5\xa5\xbd‘
>>>:
#server
电话来了
b‘1‘
b‘2‘
b‘3‘
b‘4‘
b‘as‘
b‘\xe5\xa5\xbd‘
注:在服务端,while True千万不能写在conn,address = sever.accept()前面,这个是为什么呢?因为客户端跟服务端只能实现建立一个连接,如果你把while True放在前面,则服务端接收数据后,又要重新建立一个新的等待连接,这样,客户端和服务端都会卡主。
处理多个连接
我们在客户端一起链接服务端,我们都知道,一个服务端只能跟一个客户端进行链接通信,那如果说,我这个正在通信的客户端断开跟服务端的通信,那其他的某个客户端就能跟客户端正常通信了。
多个连接的服务端:
import socket
server = socket.socket()
server.bind((‘localhost‘,11111))
server.listen()
while True: #在连接的时候加一个死循环
conn,addr = server.accept()
print(‘电话来了‘)
count = 0
while True:
data = conn.recv(1024)
if not data:break
print(data.decode())
conn.send(data)
count +=1
server.close()
注:上面if not data:break这段代码不写的后果是:当客户端断开链接时,服务端进入死循环,不断接收客户端的空数据。
多个连接的客户端:
import socket
client = socket.socket()
client.connect((‘localhost‘,11111))
while True:
msg = input(‘>>>:‘)
if len(msg) ==0:continue #这边判断输入的字符是否为空,为空就跳过
client.send(msg.encode())
data = client.recv(1024)
print(data)
client.close()
注:为防止客户输入空格主机卡死,需要对输入进行判断。
演示:
模拟ssh
客户端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
client = socket.socket()
client.connect(("localhost", 6969))
while True:
msg = input(">>>:").strip()
if len(msg) == 0: continue
client.send(msg.encode(‘utf-8‘))
data = client.recv(1024)
print(data.decode())
client.close()
服务端
import socket
import os # 导入os模块
sever = socket.socket()
sever.bind(("localhost", 6969))
sever.listen()
while True:
conn, addr = sever.accept()
print("电话来了")
while True:
data = conn.recv(1024)
if not data: break
res = os.popen((data.decode())).read() # 调用linux命令
conn.send(res.encode()) # 执行的命令返回值
sever.close()
执行结果: