类与对象 (01)
周六又进了趟城, 跟刚从北京过来的小陆学姐, 山大硕士毕业, 然后在四大干了几年, 现在腾讯一部门做战略顾问, 就是很优秀哇. 回顾了一些校园生活, 职场经历, 职场江湖, 还有一些逸闻趣事, 就聊得很嗨. 得出的几个关键结论如下:
-
企业的文化, 价值观, 战略, 受领导人影响很大
-
从适普和概率观点来看, 一个人竞争力是, 高学历 + 行业经验, 学历尤为重要
-
选择领导很重要, 就一个原则: 头脑在线, 人靠谱, 头脑(智商 + 情商) 尤为重要
-
物以类聚, 人以群分, 不得不承认, 在职场上也是这个自然法则
-
跳槽其实涨薪水最快的方式, 但前提是, 你在岗期给东家做出了成绩, 同时自己能力不断增强, 且岗位不换
-
要有职业素养, 最为重要的是, 专业性 + 责任感. 二者同等重要, 这也是凸显了教育培养的重要性
-
要有底线. 法律和道德的底线, 难免是会涉及到这些敏感区, 需要去挑战试探, 同时也要能搂住底线
感觉我还是见识不够哇, 我其实很害怕改变, 所有就基本不做商业这块, 只是做些数据, IT 这块, 比较客观, 纯粹, 可以保持住初心的一些事情, 一直在坚持着.
不扯了...继续温习下类和对象的一些旧知识, 和新的补充.
围绕 类定义相关的编程模型, 如 Python 特性, 特殊方法使用, 类封装技术, 继承, 内存管理, 设计模式这些
改变对象的字符串显示
需求
改变对象实例的打印或显示输出, 让她们更具有可读性
方案
通过重写 _ _ str _ _() 和 _ _ repr _ _ () 方法实现
class Pair:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Pair({self.x}, {self.y})'
def __str__(self):
return f'{self.x}, {self.y}'
# test
p = Pair(1, 2)
print(p)
p
1, 2
Pair(1, 2)
__ str __ () 在 print( 实例对象 obj) 时会自动调用. 即将一个对象转为一个字符串, 通常是将对象的一些属性信息给打印出来呀.
__ repr __ () 方法返回一个实例的代码表示形式, 常用用来重新构造该实例, 内置的 repr() 返回一个字符串, 通常是 repr() 和 eval () 是一个互逆的过程.
# veal: 字符串转为对象
my_str = '{"name": "youge", "age":18}'
str_to_obj = eval(my_str)
print(type(str_to_obj), str_to_obj)
# repr: 对象转字符串
obj_to_str = str_to_obj.__repr__()
print(type(obj_to_str), obj_to_str)
<class 'dict'> {'name': 'youge', 'age': 18}
<class 'str'> {'name': 'youge', 'age': 18}
让对象支持上下文管理
需求
让对象能使用上下文管理, 即用 with 语句
方案
通过实现 __ enter __ () 和 __ exit __ () 方案, 即能用 with 语句.
跟 for 迭代一样的, for 实现的是 __ iter __ 和 __ next __ 方法
from socket import socket, AF_INET, SOCK_STREAM
from functools import partial
class LasyConnection:
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
self.address = address
self.family = family
self.type = type
self.sock = None
def __enter__(self):
if self.sock is not None:
raise RuntimeError('Already connected')
# 创建套接字并连接
self.sock = socket(self.family, self.type)
self.sock.connect(self.address)
return self.sock
def __exit__(self, exc_ty, exc_val, error):
self.sock.close()
self.sock = None
# test
con = LasyConnection(('www.baidu.com', 80))
with con as s:
s.send(b'xxxxxxxx')
s.send(b'aaaaa')
resp = b''.join(iter(partial(s.recv, 8888), b''))
编写上下文管理器的主要原理是, 编写的代码会放到 with 语句块中执行.
- 首先, 当遇到 with 语句时, __ enter __ () 方法会被自动触发. 它的返回值 (如果有) 会被赋值 as 声明的变量. 如 'with open(xxx) as f ' 这个 f 的值即为 __ enter __ () 方法的返回值.
- 然后, with 语句块中的代码继续开始运行 ... (业务逻辑)
- 最后, __ exit __ () 方法会被触发, 并进行清理工作.
# 不需要 调用 close() 的原因, 因为 with 执行结束后会自动清理
with open('xxxx', 'r') as f:
pass
不论 with 代码块中发生什么, 上面的控制流都会执行完, 即便发生异常. 事实上, __ exit __ () 方法的第三个参数包含了异常类型, 异常值和追溯信息(如果有). 同时 __ exit __ () 方法能自己决定如何利用这个异常信息, 或者忽略并发返回一个 None. 如果 __ exit __ () 返回 True, 则异常会被清空, with 语句后面的话继续执行.
上面的这个版本呢, 只能允许一个 with 语句来连接, 一个 socket 显然不行, 于是来改成这样的.
from socket import socket, AF_INET, SOCK_STREAM
class LasyConnection:
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
self.address = address
self.family = family
self.type = type
self.connections = []
def __enter__(self):
# 创建套接字并连接
sock = socket(self.family, self.type)
sock.connect(self.address)
# 将每个 socket 都存起来
self.connections.append(sock)
return self.sock
def __exit__(self, exc_ty, exc_val, error):
self.connections.pop().close()
# test
con = LasyConnection(('www.baidu.com', 80))
with con as s1:
pass
with con as s2:
pass
# s1 and s2 are independent sockets
LasyConnection 类就可以看做是一个连接工厂. 在内部, 构造了一个栈 (列表) . 每次 __ enter __ 方法被调用, 就会复制一个新的连接并加入到栈中. 同时 __ exit __ 方法会从栈中 pop 出最后一个 sockets 兄弟给给关闭掉. 它可以允嵌套多个 with 语句创建多连接.
在管理一些资源文件, 网络连接, 和锁的场景中, 用上下文管理器是非常普遍的. 它们的共同特点是, 必须被手动的关闭或释放来确保程序的正确运行. 如果请求了一个锁, 则必须用后要手动释放, 不然就死锁了. 而通过 实现 __ enter __ 和 __ exit __ 方法 并用 with 语句则可很容易避免这些问题, 因为有 __ exit __ 方法安排上了.