Py003-02-03模拟ssh远程登录

模拟ssh远程登录

注意 window默认编码是gbk,linux默认是 utf-8

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import socket
import subprocess

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8082)) # ip 加端口号
phone.listen(5) # 5代表最大链接数

print('starting....')
while True: # 链接循环
conn,client_addr = phone.accept()

while True: # 通讯循环
try:
# 1 接受命令
cmd = conn.recv(1024)
# 针对客户端中断问题
if not cmd:break # 适用于linux 如果是windows就 try catch
# 2 执行命令,拿到结果
obj = subprocess.Popen(cmd.decode('gbk'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
stdout = obj.stdout.read().decode('gbk')
stderr = obj.stderr.read().decode('gbk')
# 3 把命令结果返回给客户端
data = stdout+stderr
print(len(data))
conn.send(data.encode('gbk'))
except ConnectionResetError: # 适用于windows
break
conn.close()

phone.close()

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8082)) # ip 加端口号

while True:
# 1 发命令
cmd = input('>>:').strip()
phone.send(cmd.encode('gbk'))
# 2 拿到命令结果
data = phone.recv(1024) # 1024的坑
print(data.decode('gbk'))

phone.close()

1024的坑

如果命令的返回结果超过1024的话,就会有问题了。即——粘包

我们知道结果保存在管道里,你内容超过1024的时候,多余的内容还在管道里

所以当你输入 ipconfig 的时候假设结果为2024 那么还残留1000 在管道里,你再次输入其他命令如ls的时候会先把上次剩余的内容 返回。。。

以上现象就是————————粘包

粘包的原理

1
2
3
4
5
6
7
1. 不管是recv 和send都不是直接接受对方的数据,而是操作自己的操作系统内存---》不是一个send对应一个recv
2. recv:
wait data 耗时时间长
copy data
send:
copy data
3.

粘包的触发条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
客户端
client.send('hello')
client.send('world')

服务端
res1 = conn.recv(1024) # 'helloworld'
res2 = conn.recv(1024) # ''
send间隔比较短,就会触发粘包,合并一次发送到服务端


客户端
client.send('hello')
time.sleep(5)
client.send('world')
服务端
res1 = conn.recv(1024) # 'hello'
res2 = conn.recv(1024) # 5秒后 'world'

send的间隔已经超过一次正常数据接受的时间。会分两次发送


-------------------------------------------------------
客户端
client.send('hello')
time.sleep(5)
client.send('world')
服务端
res1 = conn.recv(1) # 'h'
res2 = conn.recv(1024) # 5秒后 'ellowworld'

服务端粘包:第一次内容过多,会在下次接受多余的

Py003-02-02socket

socket

简单例子 拨打电话

服务端.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import socket

# 1 买手机
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2 绑定手机卡

phone.bind(('127.0.0.1',8081)) # ip 加端口号
# 3 开机
phone.listen(5) # 5代表最大链接数

# 4 等电话打入
print('starting....')
conn,client_addr = phone.accept()

# 5 收发消息
# 单位bytes 现在表示最大接受1024字节的数据
data = conn.recv(1024) # 1024代表接受数据的最大数
print('客户端的数据',data)
conn.send(data.upper())

# 6 挂电话
conn.close()

# 7 关机
phone.close()

客户端.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import socket

# 1 买手机
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 2 拨号
phone.connect(('127.0.0.1',8081)) # ip 加端口号

# 3 发/收消息
phone.send('hello'.encode('utf-8'))
data = phone.recv(1024)
print(data)

# 4 关闭
phone.close()

通讯循环

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8081)) # ip 加端口号
phone.listen(5) # 5代表最大链接数

print('starting....')
conn,client_addr = phone.accept()

while True: # 通讯循环
data = conn.recv(1024)
print('客户端的数据',data)
conn.send(data.upper())

conn.close()

phone.close()

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect(('127.0.0.1',8081)) # ip 加端口号

while True:
msg = input('>>:').strip()
phone.send(msg.encode('utf-8'))
data = phone.recv(1024)
print(data)

phone.close()

重复启动服务端的bug

因为端口重复占用,所以必须收到切换端口

1
2
3
4
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 添加这一行代表重用端口
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.connect(('127.0.0.1',8081)) # ip 加端口号

客户端直接敲回车发数据的bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect(('127.0.0.1',8081)) # ip 加端口号

while True:
msg = input('>>:').strip()
# bug解决
if not msg:continue
phone.send(msg.encode('utf-8'))
data = phone.recv(1024)
print(data)

phone.close()

客户端意外中断,服务器端在linux里会造成死循环的bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8081)) # ip 加端口号
phone.listen(5) # 5代表最大链接数

print('starting....')
conn,client_addr = phone.accept()

while True: # 通讯循环
try:
data = conn.recv(1024)
# 针对客户端中断问题
if not data:break # 适用于linux 如果是windows就 try catch
print('客户端的数据',data)
conn.send(data.upper())
except ConnectionResetError: # 适用于windows
break

conn.close()

phone.close()

在不会并发的情况下——一个一个的服务用户

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8081)) # ip 加端口号
phone.listen(5) # 5代表最大链接数

print('starting....')
while True: # 链接循环
conn,client_addr = phone.accept()

while True: # 通讯循环
try:
data = conn.recv(1024)
# 针对客户端中断问题
if not data:break # 适用于linux 如果是windows就 try catch
print('客户端的数据',data)
conn.send(data.upper())
except ConnectionResetError: # 适用于windows
break
conn.close()

phone.close()

服务一个客户就要把对应客户端终止才能服务下一个客户

Py003-02-01网络

网络协议

os七层:

  • 应——应用层
  • 表——表示层
  • 会——会话层
  • 传——传输层
  • 网——网络层
  • 数——数据链路层
  • 物——物理层

也叫五层协议

把 应用层/表示层/会话层 统一表示为——应用层

五层协议详解

数据报:有报头和报文 报头代表头信息 报文是实际的数据内容

  • 物理层

物理设备,负责发电信号——形如01010101的二进制内容通过网线

  • 数据链路层

以太网协议:一组数据帧称为一个数据报,基于mac地址以广播的形式(只能在局域网里)

  • 网络层

以太网通过mac只能表示同一局域网的一台机子

而ip地址可以标识一个子网的地址

IP协议:也是数据报的形式:形如 ip头+data

最后ip+mac就可以标识全世界独一无二的一台机器

  • 传输层

tcp/udp

  • 应用层

各种软件自己的协议 http/ftp

1
2
3
4
5
6
7
8
9
10
11
12
13
14


客户端: 服务端:
应用层 http头 + data 应用层 http头 + data
↓封包 ↑解包
传输层 tcp头 + (http头 + data) 传输层 tcp头 + (http头 + data)
↓封包 ↑解包
网络层 ip头 + (tcp头 + (http头 + data)) 网络层 ip头 + (tcp头 + (http头 + data))
↓封包 ↑解包
数据链路层 以太网头 + (ip头 + (tcp头 + (http头 + data))) 数据链路层 以太网头 + (ip头 + (tcp头 + (http头 + data)))
↓ ↑
物理层 物理层
↓ ↑
→→→→→→→→→→→→→→→→→→→→→→→→网络→→→→→→→→→→→→→→→→→→→→→→→→

tcp三次握手

  1. 客户端:我能连接你吗?
  2. 服务端:我准备好了,你连接吧!
  3. 客户端:好!我连接你了

tcp协议/udp协议

tcp协议

  • 可靠的:面向连接,双向通道
  • 传输速度慢

生活的实例:你往水里扔石头看见了浪花

udp协议

  • 不可靠:不面向连接
  • 直接发送数据是否收到数据未知,数据可能丢
  • 传输速度快

生活的实例:你往草里扔石头没啥反应

Py003-01-15异常操作方式

异常的处理

多分支

被监测的代码块抛出的异常有多种可能性,并且我们需要针对每一种异常类型都定制专门的处理逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
try:
print('===>1')
# name
print('===>2')
l=[1,2,3]
# l[100]
print('===>3')
d={}
d['name']
print('===>4')

except NameError as e:
print('--->',e)

except IndexError as e:
print('--->',e)

except KeyError as e:
print('--->',e)

print('====>afer code')

万能异常:Exception

  • 被监测的代码块抛出的异常有多种可能性,
  • 并且我们针对所有的异常类型都只用一种处理逻辑就可以了,那就使用Exception
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try:
print('===>1')
# name
print('===>2')
l=[1,2,3]
l[100]
print('===>3')
d={}
d['name']
print('===>4')

except Exception as e:
print('异常发生啦:',e)

print('====>afer code')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
try:
print('===>1')
# name
print('===>2')
l=[1,2,3]
# l[100]
print('===>3')
d={}
d['name']
print('===>4')

except NameError as e:
print('--->',e)

except IndexError as e:
print('--->',e)

except KeyError as e:
print('--->',e)

except Exception as e:
print('统一的处理方法')


print('====>afer code')

其他结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
try:
print('===>1')
# name
print('===>2')
l=[1,2,3]
# l[100]
print('===>3')
d={}
d['name']
print('===>4')

except NameError as e:
print('--->',e)

except IndexError as e:
print('--->',e)

except KeyError as e:
print('--->',e)

except Exception as e:
print('统一的处理方法')

else:
print('在被检测的代码块没有发生异常时执行')

finally:
print('不管被检测的代码块有无发生异常都会执行')



print('====>afer code')

# 回收资源

try:
f=open('a.txt','r',encoding='utf-8')
print(next(f))
print(next(f))
print(next(f))
print(next(f))

print(next(f))
print(next(f))
finally:
f.close()

主动触发异常:raise 异常类型(值)

1
2
3
4
5
6
7
8
9
10
class People:
def __init__(self,name,age):
if not isinstance(name,str):
raise TypeError('名字必须传入str类型')
if not isinstance(age,int):
raise TypeError('年龄必须传入int类型')
self.name=name
self.age=age

p=People('egon',18)

自定义异常类型

1
2
3
4
5
6
7
8
9
class MyException(BaseException):
def __init__(self,msg):
super(MyException,self).__init__()
self.msg=msg

def __str__(self):
return '<%s>' %self.msg

raise MyException('我自己的异常类型') #print(obj)

断言assert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
info={}
info['name']='hjx'
# info['age']=18


# if 'name' not in info:
# raise KeyError('必须有name这个key')
#
# if 'age' not in info:
# raise KeyError('必须有age这个key')

assert ('name' in info) and ('age' in info)



if info['name'] == 'hjx' and info['age'] > 10:
print('welcome')

万能异常(铁定不出错)

1
2
3
4
5
6
try:
pass


except Exception:
pass

Py003-01-14异常处理

什么是异常

异常是错误发生的信号,一旦程序出错,并且程序没有处理这个错误,那个就会抛出异常,并且程序的运行随之终止

1
2
3
4
5
6
7
print('1')
print('2')
print('3')
int('aaaa')
print('4')
print('5')
print('6')

错误分为两种:

  • 语法错误:在程序执行前就要立刻改正过来
1
2
print('xxxx'
if 1 > 2
  • 逻辑错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ValueError
# int('aaa')

#NameError
# name

#IndexError
# l=[1,2,3]
# l[1000]

#KeyError
# d={}
# d['name']


#AttributeError
# class Foo:
# pass
#
# Foo.xxx


#ZeroDivisionError:
# 1/0


#TypeError:int类型不可迭代
# for i in 3:
# pass

# import time
# time.sleep(1000)

异常

强调一:错误发生的条件如果是可以预知的,此时应该用if判断去预防异常
1
2
3
4
5
6
7
AGE=10
age=input('>>: ').strip()

if age.isdigit():
age=int(age)
if age > AGE:
print('太大了')
强调二:错误发生的条件如果是不可预知的,此时应该用异常处理机制,try…except
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
try:
f=open('a.txt','r',encoding='utf-8')

print(next(f),end='')
print(next(f),end='')
print(next(f),end='')
print(next(f),end='')

print(next(f),end='')
print(next(f),end='')
print(next(f),end='')

f.close()
except StopIteration:
print('出错啦')


print('====>1')
print('====>2')
print('====>3')

Py003-01-13元类

选学内容(基本用不到)

元类介绍

储备知识 exec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
参数1 :字符串形式的命令
参数2 :全局作用域(字典形式)如果不指定默认是globals()
参数3 :局部作用域(字典形式)如果不指定默认是locals()

g = {
"x":1,
"y":2
}

l = {}

exec('''
global x,m
x = 10
m = 100

z = 3
''',g,l)

print(g) # 全局作用域 x的值修改为 10 新增 m = 100
print(l) # 局部作用域里 l 新增了 z:3

一切皆对象,对象可以怎么用?

  1. 都可以被引用,x=obj
  2. 都可以当作函数的参数传入
  3. 都可以当作函数的返回值
  4. 都可以当作容器类的元素,l=[func,time,obj,1]

类也是对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Foo():
pass

obj = Foo()
print(type(obj))
print(type(Foo))

# <class '__main__.Foo'>
# <class 'type'>

class Bar():
pass

print(type(Bar)) # <class 'type'>

产生类的类称之为元类,默认所有用class定义的类,它们的元类是type

定义类的两种方式

  • class关键字
  • type元类产生
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# class方式
class Chinese: #Chinese=type(...)
country='China'

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

def talk(self):
print('%s is talking' %self.name)

# type方式
# 定义类的三要素:类名,类的基类们,类的名称空间
class_name='Chinese'
class_bases=(object,)

class_body="""
country='China'

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

def talk(self):
print('%s is talking' %self.name)
"""

class_dic={}
exec(class_body,globals(),class_dic)

# 定义类的三要素:类名,类的基类们,类的名称空间
Chinese1=type(class_name,class_bases,class_dic)

自定义元类控制类的创建

  • 类名必须大写
  • 必须有文档注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')

if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')

super(Mymeta,self).__init__(class_name,class_bases,class_dic)

class Chinese(object,metaclass=Mymeta):
'''
中文人的类
'''
country='China'

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

def talk(self):
print('%s is talking' %self.name)

自定义元类控制类的实例化行为

知识储备call方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Foo():
pass

obj = Foo()
# 请问obj能当作函数调用吗?
obj(1,2,3) # 报错
# --------------------------------------
# 需要提供call方法
# 再看

class Foo2():
def __call__(self,*args,**kwargs):
print('===>')
pass

obj2= Foo2()
obj2(1,2,3) # 不报错

切入正题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')

if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')

super(Mymeta,self).__init__(class_name,class_bases,class_dic)

def __call__(self, *args, **kwargs): #obj=Chinese('hjx',age=18)
# print(self) #self=Chinese
# print(args) #args=('hjx',)
# print(kwargs) #kwargs={'age': 18}

#第一件事:先造一个空对象obj
obj=object.__new__(self)
#第二件事:初始化obj
self.__init__(obj,*args,**kwargs)
#第三件事:返回obj
return obj

class Chinese(object,metaclass=Mymeta):
'''
中文人的类
'''
country='China'

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

def talk(self):
print('%s is talking' %self.name)




obj=Chinese('egon',age=18) #Chinese.__call__(Chinese,'hjx',18)

print(obj.__dict__)

元类控制类的实例化行为

单例模式

实现方式一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class MySQL:
__instance=None #__instance=obj1

def __init__(self):
self.host='127.0.0.1'
self.port=3306

@classmethod
def singleton(cls):
if not cls.__instance:
obj=cls()
cls.__instance=obj
return cls.__instance


def conn(self):
pass

def execute(self):
pass


# obj1=MySQL()
# obj2=MySQL()
# obj3=MySQL()
#
# print(obj1)
# print(obj2)
# print(obj3)

obj1=MySQL.singleton()
obj2=MySQL.singleton()
obj3=MySQL.singleton()

print(obj1 is obj3)

实现方式二:元类的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')

if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')

super(Mymeta,self).__init__(class_name,class_bases,class_dic)
self.__instance=None

def __call__(self, *args, **kwargs): #obj=Chinese('egon',age=18)
if not self.__instance:
obj=object.__new__(self)
self.__init__(obj)
self.__instance=obj

return self.__instance



class Mysql(object,metaclass=Mymeta):
'''
mysql xxx
'''
def __init__(self):
self.host='127.0.0.1'
self.port=3306

def conn(self):
pass

def execute(self):
pass



obj1=Mysql()
obj2=Mysql()
obj3=Mysql()

print(obj1 is obj2 is obj3)

Py003-01-12内置方法

内置方法

  • isinstance(对象,类名) 谁是谁的实例
  • issubclass(sub,super) 这个类是不是另一个类的子类

item系列

将类定制成像字典一样的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Foo:
def __init__(self,name):
self.name = name

def __getitem__(self, item):
print('get item')
self.__dict__
print(item)
return self.__dict__.get(item)

def __setitem__(self, key,value):
print('set item')
print(key,value)
self.__dict__[key] = value

def __delitem__(self, key):
print('del item')
print(key)
del self.__dict__[key]

obj = Foo('hjx')
print(obj.__dict__)
# 属性名
print(obj['name']) # obj.name
print(obj['namexxx'] ) # 不会报错


obj['sex'] = 'man'
print(obj['sex'])

del obj['name']
print(obj.__dict__)

双下str双下

1
2
3
4
5
6
7
8
9
10
11
class People:
def __init__(self,name,age):
self.name = name
self.age = age

def __str__(self):
return '<name:%s,age:%s>'%(self.name,self.age)

p = People('hjx',18)

print(p) # <name:hjx,age:18>

双下del双下

1
2
3
4
5
6
f = open('aa.py',mode='w+')
f.read()
f.close() # 回收操作系统资源

# 此时f还存在不存在——存在
print(f) # <_io.TextIOWrapper name='aa.py' mode='w+' encoding='cp936'>

再看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Open:
def __init__(self,fileName):
print('open file...')
self.fileName = fileName

def __del__(self):
print('del_____')

f2 = Open('aa.txt')
# del f2
print('-----main------')
'''
open file...
-----main------
del_____
'''

---------------------
f3 = Open('aa.txt')
del f3
print('-----main------')

'''
open file...
del_____
-----main------
'''

执行结果:你不调用 del程序会在执行结束后自动帮你调用 ,如果你手动调用了,就不帮你调用了

Py003-01-11反射

反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class People:
def __init__(self,name,age):
self.name = name
self.age = age

def talk(self):
print('%s is talking '%self.name)


obj = People('hjx',18)


# 判断是否含有属性和方法
print(hasattr(obj,'name'))
print(hasattr(obj,'talk'))

# 拿到对象的属性或方法
# print(getattr(obj,'namexxxx')) 报错因为 没有该属性
# 兜底写法
print(getattr(obj,'namexxxx',None))


# 修改对象的属性
setattr(obj,'sex','man')
print(obj.sex)

# 删除对象的属性
delattr(obj,'age')
print(obj.__dict__)

# 类的属性也是可以获取的
print(getattr(People,'city'))

Py003-01-10绑定方法与非绑定方法

绑定方法与非绑定方法

在类内部定义的方法,分为两类

  1. 绑定方法:绑定给谁,就应该由谁来调用,谁来调用就会把调用者当作第一个参数自动传入

    • 绑定到对象的方法:在类内定义的没有被任何装饰器修饰的

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      class Foo:
      def __init__(self,name):
      self.name = name
      def tell(self):
      print('名字是:%s'%self.name)

      f = Foo('hjx')
      print(Foo.tell) # <function Foo.tell at 0x0000027D2DE7AEA0>
      Foo.tell(f) # 类可以调用tell方法,需要传递实例对象

      print(f.tell) # <bound method Foo.tell of <__main__.Foo object at 0x00000197433C95C0>>
    • 绑定到类的方法:在类内定义的同时被装饰器classmethod修饰的方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      class Foo:
      def __init__(self,name):
      self.name = name

      @classmethod
      def func(cls):
      print(cls)
      f = Foo('hjx')

      print(Foo.func) # <bound method Foo.func of <class '__main__.Foo'>>
      Foo.func() # <class '__main__.Foo'>
  2. 非绑定方法:没有自动传值那么一说

    • 非绑定方法:不与类和对象绑定

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      class Foo:
      def __init__(self,name):
      self.name = name

      @staticmethod
      def sum(x,y):
      return x+y

      f = Foo('hjx')

      print(f.sum)
      print(Foo.sum)
      f.sum(1,2)
      Foo.sum(2,5)

      '''
      <function Foo.sum at 0x000002240B6A2378>
      <function Foo.sum at 0x000002240B6A2378>
      3
      7
      '''

使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import hashlib
import time
settings = {
"name":"hjx2",
"age":28,
"sex":'man'
}

class People:
def __init__(self,name,age,sex):
self.id = self.create_id()
self.name = name
self.age = age
self.sex = sex

def tellInfo(self): # 绑定到对象的方法
print('name is %s,age: %s,sex :%s'%(self.name,self.age,self.sex))

@classmethod
def from_conf(cls):
obj = cls(
settings["name"],
settings["age"],
settings["sex"]
)
return obj

@staticmethod
def create_id():
m = hashlib.md5(str(time.time()).encode('utf-8'))
return m.hexdigest()


p = People('hjx',18,'man')

# 绑定给对象,就应该由对象调用,自动将对象本身当第一个参数传入
p.tellInfo()

# 绑定给类,就应该由类来调用,自动将类本身当作第一个参数传入
p2 = People.from_conf()
p2.tellInfo()

# 非绑定方法,不与类和对象绑定,谁都可以调用,没有自动传值一说
p3 = People.from_conf()
print(p3.id)

Py003-01-09proterty

property的使用

房屋面积的需求

1
2
3
4
5
6
7
8
9
10
11
12
class Room:
def __init__(self,long_val,width_val):
self.long_val = long_val
self.width_val = width_val
# 房屋的面积
def Area(self):
return self.long_val * self.width_val

r = Room(100,100)

# 我想要房屋的面积数据,但是要通过方法一样调用 不能像 r.long_val一样去获取
print(r.Area())

property的使用

  • 使使用者感知不到他在调用方法,它以为是在调用属性

添加property装饰器后,像访问属性一样访问数据——本质依然是触发方法Area的执行

1
2
3
4
5
6
7
8
9
10
11
12
13
class Room:
def __init__(self,long_val,width_val):
self.long_val = long_val
self.width_val = width_val
@property
def Area(self):
return self.long_val * self.width_val

r = Room(100,100)
print(r.Area)

# Area能像属性一样赋值吗?
# r.Area = 2000 报错 AttributeError: can't set attribute

propety的补充(了解就行)

突然你就想像数据一样,给Area赋值呢? 可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class People:
def __init__(self,name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self,val):
if not isinstance(val,str):
print('名字必须是字符串')
return
self.__name = val

@name.deleter
def name(self):
print('delete 失败')

p = People('hjx')
print(p.name)
p.name = '李四'
print(p.name)

del p.name