python代码规范之异常,规则,函数返回值使用解读

Written by

in

文章目录
  • 1: 只做最精准的异常捕获,每个异常捕获只处理一个容易错的地方。 2:避免抛出高于当前抽象级别的异常外,我们同样应该避免泄露低于当前抽象级别的异常。 3:连续异常,注意使用上下文管理器。 def upload_avatar(request): “””用户上传新头像””” try: avatar_file = request.FILES[‘avatar’] except KeyError: raise error_codes.AVATAR_FILE_NOT_PROVIDED try: resized_avatar_file = resize_avatar(avatar_file) except FileTooLargeError as e: raise error_codes.AVATAR_FILE_TOO_LARGE except ResizeAvatarError as e: raise error_codes.AVATAR_FILE_INVALID try: request.user.avatar = resized_avatar_file request.user.save() except Exception: raise error_codes.INTERNAL_SERVER_ERROR return HttpResponse({}) 下面代码连续的异常,让我们看到眼花缭乱。 使用上下文管理器管理异常: class Resource(object): def __enter__(self): print(“连接资源”) return self def __exit__(self, exc_type, exc_val, exc_tb): print(“关闭资源”) return True def operate(self): 1 / 0 with Resource() as res: res.operate() # 不会报错 什么是上下文管理器? 答: 一个类实现了__enter__方法和__exit__方法,则这个类的实例就是上下文管理器。 上下文管理器的强大在于__exit__中你可以决定是抛出异常还是在这里解决。
  • 1: 单个函数,返回值不要有多种类型。 2: partial函数构造函数: def multiply(x, y): return x * y def double(value): # 返回另一个函数调用结果 return multiply(2, value) from functools import partial # partial函数是专门用来基于一个函数来构造一个新的函数的。 # 参数1: 函数名 # 其余参数就是被基于的函数的参数。 def multiply(x, y): return x * y double = partial(multiply, 2) 3: 不要返回结果和错误,而是选择抛出异常。 4:谨慎使用None返回: None作为默认返回值(没有问题) None作为意料之中的返回值(没有问题) None作为调用失败返回值, 不推荐使用,更改为异常捕获方式。 5:合理返回空对象: 对于返回为None的地方,我们在接受返回值的地方就要使用异常捕获,判断是否为None,这非常的麻烦。 Django 框架里的 AnonymousUser 就是一个典型的 null object。 原理: 就是使用一个符合正常结果接口的“空类型”来替代空值返回/抛出异常,以此来降低调用方处理结果的成本。 案例: class Account: # def __init__ 已省略… … @classmethod def from_string(cls, s): “””从字符串初始化一个账号 :returns: 如果输入合法,返回 Account object,否则返回 NullAccount “”” try: username, balance = s.split() balance = decimal.Decimal(float(balance)) except ValueError: return NullAccount() if balance < 0: return NullAccount() return cls(username=username, balance=balance) class NullAccount: username = ” balance = 0 @classmethod def from_string(cls, s): raise NotImplementedError 6:返回生成器,而不是返回列表。 # 返回列表 def test01(my_list): res_list = [] for item in my_list: res_list.append(item * 2) return res_list # 返回生成器 def test02(my_list): for item in my_list: yield item * 2 my_list = [1, 2, 3, 4, 5] print(test01(my_list)) # [2, 4, 6, 8, 10] print(test02(my_list)) # <generator object test02 at 0x000001A6C693A2A0> for item in test02(my_list): print(item) 7:尽量不要使用递归: python不支持尾递归优化。 python递归有最大层数限制。
  • 以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。 您可能感兴趣的文章: Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧 Python递归函数返回值为None问题及解决 python3如何获取子线程中函数返回值 python之plt.hist函数的输入参数和返回值的用法解释 关于python函数的建立、调用、传参、返回值详解 Python return函数返回值类型和帮助函数使用教程
  • 目录
    • 一:python的规则
      • 1:集合的规则
      • 2:__format__对象字符串格式化
      • 3:__getitem__方法定义容器
    • 二:python异常处理三个习惯
      • 三:函数返回建议
        • 总结

          • 规则: 如果要把一个东西装到集合中去,这个东西必须是可以哈希的
          • 可变类型都是不可哈希的。(列表, 集合,字典),本质就是哈希值不固定。
          • 思维1: 对于存在A,不存在B等问题,思维定式就是采用集合思维处理。
          • 思维2: 我们可以自定义哈希对象

          案例:

          给定两个列表套字典的数据,数据内容是人员(姓名,电话相同,则默认就是同一个人),我们要求是找出存在A列表,但是不存在B列表的人员。

          list_a = [
              {'first_name': 'Ren', 'last_name': 'shanshan', 'phone_num': '12345678'},
            {'first_name': 'Wang', 'last_name': 'gang', 'phone_num': '12323338'},
              {'first_name': 'Li', 'last_name': 'ruixue', 'phone_num': '123389278'},
          ]
          
          list_b = [
              {'first_name': 'Ren', 'last_name': 'shanshan', 'phone_num': '12345678'},
              {'first_name': 'Wang', 'last_name': 'gang', 'phone_num': '12323338'},
              {'first_name': 'Xu', 'last_name': 'daer', 'phone_num': '125545678'},
          ]
          
          
          class People(object):
          
              def __init__(self, first_name, last_name, phone_num):
                  self.first_name = first_name
                  self.last_name = last_name
                  self.phone_num = phone_num
          
              def __hash__(self):
                  # 默认是内存地址的哈希,这里我们改成名字+电话这样的一个元组的哈希值。
                  return hash(
                      (self.first_name, self.last_name, self.phone_num)
                  )
          
              def __eq__(self, other):
                  # 如果传入的对象是当前类的对象,并且哈希值两者相等,则返回True。
                  if isinstance(other, People) and hash(other) == hash(self):
                      return True
                  return False
          
          # 将字典构造成哈希对象,然后加入集合中
          set_1 = set(People(**p) for p in list_a)
          set_2 = set(People(**p) for p in list_b)
          # 使用集合的差
          end_set = set_1 - set_2
          
          for item in end_set:
              print(item.first_name, item.last_name, item.phone_num)
              # Li ruixue 123389278
          
          

          使用python3.7版本之后的数据类: dataclasses。

          from dataclasses import dataclass
          
          @dataclass(unsafe_hash=True)
          class People(object):
              first_name: str
              last_name: str
              phone_num: str
          
          
          set_1 = set(People(**p) for p in list_a)
          set_2 = set(People(**p) for p in list_b)
          
          end_set = set_1 - set_2
          
          for item in end_set:
              print(item.first_name, item.last_name, item.phone_num)
              # Li ruixue 123389278
          
          

          1: 需求: 对于自己定义的People对象,打印出不同的信息。

          class People(object):
          
              def __init__(self, name, age):
                  self.name = name
                  self.age = age
          
              def __format__(self, format_spec):
                  if format_spec == 'long':
                      return f'{self.name} is {self.age} years old!'
                  elif format_spec == 'simple':
                      return f'{self.name} ({self.age})'
                  raise ValueError('invalid format spec')
          
          
          p = People(name='liangshan', age=23)
          print('{0:simple}'.format(p))
          # liangshan (23)
          print('{0:long}'.format(p))
          # liangshan is 23 years old!
          

          1: 如果我想定义一个容器,那么肯定要考虑的就是容器取长度的方法,容器取第几个元素方法。

          class Events:
              def __init__(self, events):
                  self.events = events
          
              def __len__(self):
                  """自定义长度,将会被用来做布尔判断"""
                  return len(self.events)
          
              def __getitem__(self, index):
                  """自定义切片方法"""
                  # 直接将 slice 切片对象透传给 events 处理
                  return self.events[index]
          
          
          events = Events([
              'computer started',
              'os launched',
              'docker started',
              'os stopped',
          ])
          
          # 理解: if events 对调用里面的__len__方法,通过返回是否是0来判断是否是None
          # events使用切片的时候,会调用events的__getitem__方法,
          # 而我们直接调用self的切片操作,相当于又调用了list中的__getitem__方法。也就是说
          # 只要是实例化Events的时候,是可以使用切片的,则这个类的对象也是可以使用切片的。
          if events:
              print(events[1:3])
          
          

          1: 只做最精准的异常捕获,每个异常捕获只处理一个容易错的地方。

          2:避免抛出高于当前抽象级别的异常外,我们同样应该避免泄露低于当前抽象级别的异常。

          3:连续异常,注意使用上下文管理器。

          def upload_avatar(request):
              """用户上传新头像"""
              try:
                  avatar_file = request.FILES['avatar']
              except KeyError:
                  raise error_codes.AVATAR_FILE_NOT_PROVIDED
          
              try:
                 resized_avatar_file = resize_avatar(avatar_file)
              except FileTooLargeError as e:
                  raise error_codes.AVATAR_FILE_TOO_LARGE
              except ResizeAvatarError as e:
                  raise error_codes.AVATAR_FILE_INVALID
          
              try:
                  request.user.avatar = resized_avatar_file
                  request.user.save()
              except Exception:
                  raise error_codes.INTERNAL_SERVER_ERROR
              return HttpResponse({})
          

          下面代码连续的异常,让我们看到眼花缭乱。

          使用上下文管理器管理异常:

          class Resource(object):
          
              def __enter__(self):
                  print("连接资源")
                  return self
          
              def __exit__(self, exc_type, exc_val, exc_tb):
                  print("关闭资源")
                  return True
          
              def operate(self):
                  1 / 0
          
          
          with Resource() as res:
              res.operate()
          # 不会报错
          
          • 什么是上下文管理器?
          • 答: 一个类实现了__enter__方法和__exit__方法,则这个类的实例就是上下文管理器。
          • 上下文管理器的强大在于__exit__中你可以决定是抛出异常还是在这里解决。

          1: 单个函数,返回值不要有多种类型

          2: partial函数构造函数

          def multiply(x, y):
              return x * y
          def double(value):
              # 返回另一个函数调用结果
              return multiply(2, value)
          
          from functools import partial
          # partial函数是专门用来基于一个函数来构造一个新的函数的。
          # 参数1: 函数名
          # 其余参数就是被基于的函数的参数。
          def multiply(x, y):
              return x * y
          
          
          double = partial(multiply, 2)
          

          3: 不要返回结果和错误,而是选择抛出异常

          4:谨慎使用None返回

          • None作为默认返回值(没有问题)
          • None作为意料之中的返回值(没有问题)
          • None作为调用失败返回值, 不推荐使用,更改为异常捕获方式

          5:合理返回空对象

          • 对于返回为None的地方,我们在接受返回值的地方就要使用异常捕获,判断是否为None,这非常的麻烦。
          • Django 框架里的 AnonymousUser 就是一个典型的 null object。

          原理:

          就是使用一个符合正常结果接口的“空类型”来替代空值返回/抛出异常,以此来降低调用方处理结果的成本。

          案例:

          class Account:
              # def __init__ 已省略... ...
              
              @classmethod
              def from_string(cls, s):
                  """从字符串初始化一个账号
          
                  :returns: 如果输入合法,返回 Account object,否则返回 NullAccount
                  """
                  try:
                      username, balance = s.split()
                      balance = decimal.Decimal(float(balance))
                  except ValueError:
                      return NullAccount()
          
                  if balance < 0:
                      return NullAccount()
                  return cls(username=username, balance=balance)
          
          
          class NullAccount:
              username = ''
              balance = 0
          
              @classmethod
              def from_string(cls, s):
                  raise NotImplementedError
          

          6:返回生成器,而不是返回列表

          # 返回列表
          def test01(my_list):
              res_list = []
              for item in my_list:
                  res_list.append(item * 2)
              return res_list
          
          
          # 返回生成器
          def test02(my_list):
              for item in my_list:
                  yield item * 2
          
          
          my_list = [1, 2, 3, 4, 5]
          print(test01(my_list))
          # [2, 4, 6, 8, 10]
          print(test02(my_list))
          # <generator object test02 at 0x000001A6C693A2A0>
          for item in test02(my_list):
              print(item)
          

          7:尽量不要使用递归

          • python不支持尾递归优化。
          • python递归有最大层数限制。

          以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。

          您可能感兴趣的文章:

          • Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧
          • Python递归函数返回值为None问题及解决
          • python3如何获取子线程中函数返回值
          • python之plt.hist函数的输入参数和返回值的用法解释
          • 关于python函数的建立、调用、传参、返回值详解
          • Python return函数返回值类型和帮助函数使用教程

          站内搜索