(十九)类的约束,异常,MD5,日志

. 类的约束

 ⾸先, 你要清楚约束是对类的约束比如现在你是⼀个项⽬经理然后呢你给⼿下的⼈分活张三你处理⼀下普通⽤户登录, 李四你处理⼀下会员登录, 王五你处理⼀下管理员登录那这个时候呢,他们就开始分别去写他们的功能了但是呢你要知道, 程序员不⼀定会有那么好的默契很有可能三个⼈会写完全三个不同的⽅法就比如这样

class Normal: # 张三, 普通⼈登录
 	def login(self):
 	pass
class Member: # 李四, 会员登录
 	def denglu(self):
 		pass
class Admin: # 王五, 管理员登录
 	def login(self):
 		Pass

  

然后呢, 他们把这样的代码交给你了你看了⼀眼张三和王五还算OK 。这个李四写的是什么⿁? denglu.......难受不但是好⽍能⽤还能凑合但是这时你这边要使⽤了问题就来了

# 项⽬经理写的总⼊⼝
def login(obj):
  print("准备验证码.......")
  obj.login()
  print("进⼊主⻚.......")

  

对于张三和王五的代码没有问题但是李四的你是不是调⽤不了那如何避免这样的问题呢? 我们要约束程序的结构也就是说在分配任务之前就应该把功能定义好然后分别交给底下的程序员来完成相应的功能

# 项⽬经理写的总⼊⼝
def login(obj):
 	print("准备验证码.......")
 	obj.login()
 	print("进⼊主⻚.......")

  

对于张三和王五的代码没有问题但是李四的你是不是调⽤不了那如何避免这样的问题呢? 我们要约束程序的结构也就是说在分配任务之前就应该把功能定义好然后分别交给底下的程序员来完成相应的功能python中有两种办法来解决这样的问题:

1. 提取⽗类然后在⽗类中定义好⽅法在这个⽅法中什么都不⽤⼲就抛⼀个异常就可以了这样所有的⼦类都必须重写这个⽅法否则访问的时候就会报错

2. 使⽤元类来描述⽗类在元类中给出⼀个抽象⽅法这样⼦类就不得不给出抽象⽅法的具体实现也可以起到约束的效果

 ⾸先我们先看第⼀种解决⽅案: ⾸先, 提取⼀个⽗类在⽗类中给出⼀个⽅法并且在⽅法中不给出任何代码直接抛异常

class Base:
   def login(self):
 	raise Exception("你没有实现login⽅法()")
class Normal(Base):
   def login(self):
 	pass
class Member(Base):
   def denglu(self):
 	pass
class Admin(Base):
   def login(self):
 	Pass

# 项⽬经理写的总⼊⼝
def login(obj):
 	print("准备验证码.......")
 	obj.login()
 	print("进⼊主⻚.......")
n = Normal()
m = Member()
a = Admin()
login(n)
login(m) # 报错. 
login(a)

  

   在执⾏到login(m)的时候程序会报错原因是, 此时访问的login()是⽗类中的⽅法但是⽗类中的⽅法会抛出⼀个异常所以报错这样程序员就不得不写login⽅法了从⽽对⼦类进⾏了相应的约束

   在本⽰例中要注意我们抛出的是Exception异常Exception是所有异常的根我们⽆法通过这个异常来判断出程序是因为什么报的错所以最好是换⼀个比较专业的错误信息最好是换成NotImplementError,其含义是: "没有实现的错误"。这样程序员或者项⽬经理可以⼀⽬了然的知道是什么错了就好比你犯错了我就告诉你犯错了你也不知道哪⾥错了这时我告诉你, xxx错了你改也好改。

        第⼆套⽅案: 写抽象类和抽象⽅法这种⽅案相对来说比上⼀个⿇烦⼀些需要给⼤家先引入⼀个抽象的概念什么是抽象呢? 想⼀下动物的‘吃’你怎么描述? ⼀个动物到底应该怎么吃? 是不是描述不清楚这⾥动物的吃就是⼀个抽象的概念只是⼀个动作的概念没有具体实现这种就是抽象的动作换句话说我们如果写⼀个⽅法不知道⽅法的内部应该到底写什么那这个⽅法其实就应该是⼀个抽象的⽅法如果⼀个类中包含抽象⽅法那么这个类⼀定是⼀个抽象类抽象类是不能有实例的比如你看看⼀些抽象派的画作在现实中是不存在的也就⽆法建立实例对象与之相对应所以抽象类⽆法创建对象创建对象的时候会报错

   在python中编写⼀个抽象类比较⿇烦需要引入abc模块中的ABCMetaabstractmethod这两个内容来我们看⼀个例⼦:

from abc import ABCMeta, abstractmethod
# 类中包含了抽象⽅法,那此时这个类就是个抽象类. 注意: 抽象类可以有普通⽅法
class IGame(metaclass=ABCMeta):
 	# ⼀个游戏到底怎么玩⼉? 你能形容? 流程能⼀样么?
 	@abstractmethod
 	def play(self):
 	  pass
 	def turn_off(self):
       print("破游戏不玩了, 脱坑了")
class DNFGame(IGame):
 	# ⼦类必须实现⽗类中的抽象⽅法. 否则⼦类也是抽象类
 	def play(self):
 		print("dnf的玩⼉法")
# g = IGame() # 抽象类不能创建对象
dg = DNFGame()
dg.play()


#通过代码我们能发现,这⾥的IGame对DNFGame进⾏了约束,换句话说,⽗类对⼦类进⾏了约束。接下来,继续解决我们⼀开始的问题。
from abc import ABCMeta, abstractmethod。 class Base(metaclass=ABCMeta):   @abstractmethod   def login(self):     pass class Normal(Base):   def login(self):     pass class Member(Base):   def denglu(self): # 这个就没⽤了 pass   def login(self): # ⼦类对⽗类进⾏实现 pass class Admin(Base):   def login(self): pass # 项⽬经理写的总⼊⼝ def login(obj):   print("准备验证码.......")   obj.login()   print("进⼊主⻚.......") n = Normal() m = Member() a = Admin() login(n) login(m) login(a)

  

总结: 约束其实就是⽗类对⼦类进⾏约束⼦类必须要写xxx⽅法. python中约束的⽅式和⽅法有两种:

1. 使⽤抽象类和抽象⽅法, 由于该⽅案来源是javac#,所以使⽤频率还是很少的

2. 使⽤⼈为抛出异常的⽅案并且尽量抛出的是NotImplementError这样比较专, ⽽且错误比较明确(推荐)。

. 异常处理

     ⾸先, 我们先说⼀下, 什么是异常? 异常是程序在运⾏过程中产⽣的错误就好比你在回家路上突然天塌了那这个就属于⼀个异常总之就是不正常那如果程序出现了异常怎么处理呢? 来段代码:

def chu(a, b):
 	return a/b

try:#(1)
 	ret = chu(10, 0)
 	print(ret)
except Exception as e:(2)
 	print("除数不能是0")(3)
打印结果:
除数不能是0

  

可以把(1),(2),(3)行注释掉,再运行看看,就会抛出来一个异常。

try...except是什么意思呢? 尝试着运⾏代码出现了错误就执⾏except后⾯的代码在这个过程中当代码出现错误的时候系统会产⽣⼀个异常对象然后这个异常会向外抛except拦截并把接收到的异常对象赋值给e。这⾥的e就是异常对象这⾥的Exception是什么? Exception是所有异常的基类, 也就是异常的根换句话说所有的错误都是Exception的⼦类对象ZeroDivisionError(除数不可以是0), 其实就是Exception的⼦类这样写好像有点⼉问题。Exception表示所有的错误太笼统了所有的错误都会被认为是Exception。当程序中出现多种错误的时候, 就不好分类了, 最好是出什么异常就⽤什么来处理这样就更加合理了所以在try...execpt语句中还可以写更多的except

try:
 	print("各种操作....")
except ZeroDivisionError as e:
 	print("除数不能是0")
except FileNotFoundError as e:
 	print("⽂件不存在")
except Exception as e:
 	print("其他错误")

  

此时程序运⾏过程中如果出现了ZeroDivisionError就会被第⼀个except捕获如果出现了FileNotFountError就会被第⼆个except捕获如果都不是这两个异常那就会被最后的

Exception捕获总之最后的Exception就是我们异常处理的最后⼀个守⻔员这时我们最常⽤的⼀套写法接下来给出⼀个完整的异常处理写法:

try:
'''操作'''
except Exception as e:
 '''异常的⽗类,可以捕获所有的异常'''
else:
 '''保护不抛出异常的代码, 当try中⽆异常的时候执⾏'''
finally:
 '''最后总是要执⾏我,不管有没有异常'''

  

   解读: 程序先执⾏操作, 然后如果出错了会走except中的代码如果不出错, 执⾏else的代码不论出不出错最后都要执⾏finally中的语句⼀般我们⽤try...except就够⽤了顶多加上finally。finally⼀般⽤来作为收尾⼯作(例如:文件,数据库的关闭等)。

   上⾯是处理异常我们在执⾏代码的过程中如果出现了⼀些条件上的不对等根本不符合我的代码逻辑比如参数我要求你传递⼀个数字你非得传递⼀个字符串那对不起我没办法帮你处理那如何通知你呢? 两个⽅案:

 ⽅案⼀直接返回即可我不管你还不⾏么?

   ⽅案⼆.:抛出⼀个异常告诉你我不好惹乖乖的听话

   第⼀种⽅案是我们之前写代码经常⽤到的⽅案但这种⽅案并不够好⽆法起到警⽰作⽤所以以后的代码中如果出现了类似的问题直接抛⼀个错误出去那怎么抛呢? 我们要⽤到raise关键字。

def add(a, b):
 '''
 给我传递两个整数. 我帮你计算两个数的和
 :param :param a:
 :param :param b:
 :return :return:
 '''
   if not type(a) == int and not type(b) == int:
 	# 当程序运⾏到这句话的时候. 整个函数的调⽤会被中断. 并向外抛出⼀个异常.
 	raise Exception("不是整数, 朕不能帮你搞定这么复杂的运算.")
    return a + b
# 如果调⽤⽅不处理异常. 那产⽣的错误将会继续向外抛. 最后就抛给了⽤户
# add("你好", "我叫Tom")
# 如果调⽤⽅处理了异常. 那么错误就不会丢给⽤户. 程序也能正常进⾏
try:
 	add("胡辣汤", "滋滋冒油的⼤腰⼦")
except Exception as e:
 	print("报错了. ⾃⼰处理去吧")

  

  当程序运⾏到raise,程序会被中断并实例化后⾯的异常对象抛给调⽤⽅,如果调⽤⽅不处理则会把错误继续向上抛出最终抛给⽤户如果调⽤⽅处理了异常那程序可以正常的进⾏执⾏

  说了这么多异常也知道如何抛出和处理了但是我们现在⽤的都是⼈家python给的异如果某⼀天你写的代码中出现了⼀个⽆法⽤现有的异常来解决问题那怎么办呢? 别着急,python可以⾃定义异常

    ⾃定义异常: 非常简单只要你的类继承了Exception那你的类就是⼀个异常类就这么简单比如你要写⼀个男澡堂⼦程序那这时要是来个女的你怎么办? 是不是要抛出⼀个性别异常啊? 我们来完成这个案例:

# 继承Exception. 那这个类就是⼀个异常类
class GenderError(Exception):
 	pass
class Person:
 	def __init__(self, name, gender):
 		self.name = name
 		self.gender = gender
def nan_zao_tang_xi_zao(person):
 	if person.gender != "男":
 		raise GenderError("性别不对. 这⾥是男澡堂⼦")
 p1 = Person("alex", "男")
p2 = Person("eggon", "蛋")

# nan_zao_tang_xi_zao(p1)
# nan_zao_tang_xi_zao(p2) # 报错. 会抛出⼀个异常: GenderError
# 处理异常
try:
   nan_zao_tang_xi_zao(p1)
   nan_zao_tang_xi_zao(p2)
except GenderError as e:
   print(e) # 性别不对, 这⾥是男澡堂⼦
except Exception as e:
   print("反正报错了")

  

ok搞定但是, 如果是真的报错了我们在调试的时候, 最好是能看到错误源⾃于哪⾥?怎么办呢? 需要引入另⼀个模块traceback,这个模块可以获取到我们每个⽅法的调⽤信息⼜被称为堆栈信息这个信息对我们找错是很有帮助的

import traceback
# 继承Exception. 那这个类就是⼀个异常类
class GenderError(Exception):
   pass
class Person:
   def __init__(self, name, gender):
 	self.name = name
 	self.gender = gender
   def nan_zao_tang_xi_zao(person):
       if person.gender != "男":
           raise GenderError("性别不对. 这⾥是男澡堂⼦")
p1 = Person("alex", "男")
p2 = Person("eggon", "蛋")
# nan_zao_tang_xi_zao(p1)
# nan_zao_tang_xi_zao(p2) # 报错. 会抛出⼀个异常: GenderError
# 处理异常
try:
   nan_zao_tang_xi_zao(p1)
   nan_zao_tang_xi_zao(p2)
except GenderError as e:
   val = traceback.format_exc() # 获取到堆栈信息
   print(e) # 性别不对. 这⾥是男澡堂⼦
   print(val)
except Exception as e:
   print("反正报错了")
结果: 
性别不对. 这⾥是男澡堂⼦
Traceback (most recent call last):
 File "/Users/sylar/PycharmProjects/oldboy/⾯向对象/day05.py", line 155, in <module>
 nan_zao_tang_xi_zao(p2)
 File "/Users/sylar/PycharmProjeccts/oldboy/⾯向对象/day05.py", line 144, in
nan_zao_tang_xi_zao
 raise GenderError("性别不对. 这⾥是男澡堂⼦")
GenderError: 性别不对. 这⾥是男澡堂⼦

  

搞定了这样我们就能收放⾃如了当测试代码的时候把堆栈信息打印出来但是当到了线上的⽣产环境的时候把这个堆栈去掉即可

四. MD5加密

     MD5加密,来看下面代码:

import hashlib
obj = hashlib.md5()
obj.update("tom".encode("utf-8")) # 加密的必须是字节
miwen = obj.hexdigest()
print(miwen) # 34b7da764b21d298ef307d04d8152dc5

  

   固定的套路,固定的代码,就是上面这一堆。理论上,MD5是不可逆的,但是,现在有些网站,总结了大量的数据,然后推出在线逆推,所以为了保证,这些网站不能逆推,可以在使用MD5时进行加盐操作。就是在obj = hashlib.md5(b‘ajkkajkjf’),加入一段固定的字符串,这样就可以保证加密后的md5就不容易被反推了。但是一定要记住,这段加盐的字符串不可以轻易的改变。否则,前后不一,就没法保证保存的数据是否是用户需要保存的了。

五.日志

  为了解决线上程序出现的不好排查的问题,所以有了日志。线上跑着的程序出问题了,就把问题记录下来。看代码:

# filename: ⽂件名
# format: 数据的格式化输出. 最终在⽇志⽂件中的样⼦
# 时间-名称-级别-模块: 错误信息
# datefmt: 时间的格式
# level: 错误的级别权重, 当错误的级别权重⼤于等于leval的时候才会写⼊⽂件
logging.basicConfig(filename='x1.txt',
 format='%(asctime)s - %((name)s - %(levelname)s -%
(module)s: %(message)s',
 datefmt='%Y-%m-%d %H:%M:%S',
level=0) # 当前配置表示 10以上的分数会被写⼊⽂件
# CRITICAL = 50
# FATAL = CRITICAL
# ERROR = 40
# WARNING = 30
# WARN = WARNING
# INFO = 20
# DEBUG = 10
# NOTSET = 0
logging.critical("我是critical") # 50分. 最贵的
logging.error("我是error") # 40分
logging.warning("我是警告") # 警告 30
logging.info("我是基本信息") # 20
logging.debug("我是调试") # 10
logging.log(2, "我是⾃定义") # ⾃定义. 看着给分

  

简单做个测试, 应⽤⼀下

class JackError(Exception):
 pass
for i in range(10):
 try:
 if i % 3 == 0:
 raise FileNotFoundError("⽂件不在啊")
 elif i % 3 == 1:
 raise KeyError("键错了")
 elif i % 3 == 2:
 raise JackError("杰克Exception")
except FileNotFoundError:
 val = traceback.format_exc()
 logging.error(val)
 except KeyError:
 val = traceback.format_exc()
 logging.error(val)
 except JackError:
 val = traceback.format_exc()
 logging.error(val)
 except Exception:
 val = traceback.format_exc()
 logging.error(val)

  

最后, 如果你系统中想要把⽇志⽂件分开比如⼀个⼤项⽬, 有两个⼦系统, 那两个⼦系统要分开记录⽇志⽅便调试那怎么办呢? 注意⽤上⾯的basicConfig是搞不定的我们要借助⽂件助⼿(FileHandler), 来帮我们完成⽇志的分开记录。

import logging
# 创建⼀个操作⽇志的对象logger(依赖FileHandler)
file_handler = logging.FileHandler('l1.log', 'a', encoding='utf-8')
file_handler.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %
(levelname)s -%(module)s: %(message)s"))
logger1 = logging.Logger('s1', level=logging.ERROR)
logger1.addHandler(file_handler)
logger1.error('我是A系统')

# 再创建⼀个操作⽇志的对象logger(依赖FileHandler)
file_handler2 = logging.FileHandler('l2.log', 'a', encoding='utf-8')
file_handler2.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s -
%(levelname)s -%(module)s: %(message)s"))
logger2 = logging.Logger('s2', level=logging.ERROR)
logger2.addHandler(file_handler2)
logger2.error('我是B系统')

  

原文地址:https://www.cnblogs.com/asia-yang/p/10157158.html