当前位置: 首页 > news >正文

网站备案号规则苏州网站设计服务

网站备案号规则,苏州网站设计服务,深圳有哪些传媒公司,如何注册一个自己的网址清洁代码 — 学习如何编写可读、可理解且可维护的代码 高级Python编程知识 Python之常用设计模式 Advanced Programming装饰器 decorators生成器 迭代器with 上下文管理器面向对象Mixin 模式反射机制并发编程 Design Patterns设计模式分类简单工厂模式工厂模式 √抽象工厂…清洁代码 — 学习如何编写可读、可理解且可维护的代码 高级Python编程知识 Python之常用设计模式 Advanced Programming装饰器 decorators生成器 迭代器with 上下文管理器面向对象Mixin 模式反射机制并发编程 Design Patterns设计模式分类简单工厂模式工厂模式 √抽象工厂模式建造者模式 √单例模式 √适配器模式 √观察者模式 √策略模式 √模板方法模式 √ Clean Code命名 Naming注释 Comments代码格式化 Code Formatting函数 Functions控制结构 Control Structures类与对象 ClassObject设计模式 Design Patterns Clean Architecture Advanced Programming Python高级编程主要补一下我不太熟悉的知识。 装饰器 decorators 装饰器就是把很多函数的执行前后需要的公用代码提取出来构造为一个装饰器函数当其他函数想调用装饰器函数时在其他函数def的头上用装饰即可。 装饰器函数是一个高阶函数拿其他函数fun作为参数或返回值的函数它接受一个函数func作为参数内层在原始函数fun执行前后添加了一些额外的操作返回fun执行机构外层返回一内层函数wrapper。 自定义装饰器装饰器可以是多层函数的嵌套真正的装饰器接受fun函数可能会加上外层 或 内层函数接受fun的万能动态参数(*args, **kwargs)。然后我们就可以对任意自定义的fun(*args, **kwargs)函数使用装饰器了。 一般是2层嵌套外层接收fun内层接收fun的参数(*args, **kwargs)。 如最简单的一个例子装饰器welcome外层接收一个fun函数内层warpper接收万能动态参数(*args, **kwargs)在内层组织print(Welcome)和fun()的执行顺序然后内层返回fun()执行结果外层返回内层函数warpper。 def welcome(fun):def warpper(*args, **kwargs):print(Welcome)return fun(*args, **kwargs)return warpperwelcome def my_fun1(message: str) - None:print(fHello {message}. this is fun1)my_fun1(Jack)再如最常见的计时装饰器_time哪个函数需要计时直接加上装饰器_time即可而不用再单独写计时的这部分代码使代码更加简洁高效。 from time import time def _time(f):def warpper(*args, **kwargs):start time()result f(*args, **kwargs)end time()print(f{f.__name__} took {end - start} seconds)return resultreturn warpper也可以是3层嵌套为了传入装饰器函数的配置参数添加一个最外层函数接收装饰器函数的配置参数返回装饰器函数。而里面两层和原始的2层结构一模一样中层接收fun内层接收fun的参数(*args, **kwargs)。除了添加最外层用于接收装饰器函数的配置参数使用时也需要给出装饰器函数的配置参数装饰器(装饰器参数)。 def welcome(name):def decorator(fun):def warpper(*args, **kwargs):print(fWelcome {name})return fun(*args, **kwargs)return warpperreturn decoratorwelcome(Tom) def my_fun1(message: str) - None:print(fHello {message}. this is fun1)my_fun1(Jack)其中内层函数可以访问外层函数的对象这个可以访问的空间叫做闭包。 具体可以看我这篇 深度学习代码优化ConfigRegistryHook的注册器机制 Registry部分对装饰器 和 闭包有详细的介绍。 dataclass当我们希望构造一个数据类时如深度学习中的Output类使用dataclass装饰可以大大简化类的定义。 1省略__init__() class UNetOutput:def __init__(self, latents, nums10):self.latents latentsself.nums numsfrom dataclasses import dataclass dataclass class UNetOutput:latents: listnums: int 102灵活设置(frozenTrue)对象一旦创建就不可更改。 dataclass(frozenTrue) class UNetOutput:latents: listnums: int3实现了对比和排序用第一个属性排序如果第一个属性相同再比较第二个…。 res1 UNetOutput([1, 2, 3], 10) res2 UNetOutput([1, 2, 3], 10) res1 res2 # Truedataclass(orderTrue) class UNetOutput:latents: listnums: int res1 UNetOutput([1, 2, 3], 12) res2 UNetOutput([1, 2, 3], 10) l [res1, res2] l.sort() # [UNetOutput(latents[1, 2, 3], nums10), UNetOutput(latents[1, 2, 3], nums12)]如果不想用第一个属性优先排序就在.sort(keyoperator.attrgetter(nums))中指定排序优先的属性。 dataclass class UNetOutput:latents: listnums: int res1 UNetOutput([1, 2, 3], 12) res2 UNetOutput([1, 2, 3], 10) l [res1, res2] import operator l.sort(keyoperator.attrgetter(nums)) l # [UNetOutput(latents[1, 2, 3], nums10), UNetOutput(latents[1, 2, 3], nums12)]类装饰器假如我们希望实现一个装饰器类在函数执行前打印entry在函数执行后打印end装饰器类在初始化__init__的时候传入fun再定义__call__函数扮演真正的装饰的wapper函数。 class MyDecorator:def __init__(self, func):self.func funcdef __call__(self, *args, **kwargs):print(Before)self.func(*args, **kwargs)print(After)MyDecorator def test():print(Testing)test()如果希望实现前面那种3层的带装饰器配置参数的的装饰器需要在__init__传入装饰器参数在__call__传入fun函数 class MyDecorator:def __init__(self, num):self.num numdef __call__(self, func):print(self.num)self.func funcdef wrapper(*args, **kwargs):print(Before)res self.func()print(After)return resreturn wrapperMyDecorator(666) def test():print(Testing)test()生成器 迭代器 关键1yield语句是生成器的核心、2生成器与迭代器是惰性列表。 一图解释 列表/元组/字典list/tuple/dict、容器container、可迭代对象iterable、迭代器iterator、生成器generator的关系 生成器 generator构造生成器的方式有2种 生成器表达式生成器表达式和列表推导式的写法几乎一样只要把中括号[]换成圆括号()就好了 list_ [x for x in range(1,11)] # 列表推导式 generator (x for x in range(1,11)) # 生成器表达式 for i in generator:print(i)yield语句——生成器函数当一个函数包含yield语句的时候这个函数就不再是一个普通的函数了而是一个生成器函数。yield和return的本质区别是return一旦返回下次执行该函数将从头开始yield返回后下次再次调用该函数时将从上次yield返回的下一句接着执行。 如下例子调用生成器函数返回一个生成器1使用next(生成器)每次从生成器中取出一次数据即执行到下一个yield 2当然生成器可以理解为一个可迭代对象使用for循环遍历。 def yield_list():print(step 1)yield 1print(step 2)yield 2print(step 3)yield 3print(step 4)yield 4generator yield_list() next(generator)generator yield_list() for value in generator:print(value)更简洁的写法 def yield_list():for i in range(1,5):print(fstep {i})yield igenerator yield_list() next(generator)generator yield_list() for value in generator:print(value)迭代器 iterator主要有2个来源1生成器本质就是迭代器其最大特点是代码简洁。2可迭代对象使用iter(可迭代对象)得到迭代器。3自定义迭代器任何实现了__iter__()和__next__()方法的对象都是迭代器__iter__()返回迭代器自身__next__()返回容器中的下一个值如果容器中没有更多元素了则抛出StopIteration异常。 迭代器是一个带状态的对象它能在你调用__next__()方法时返回容器中的下一个值。 class My_Iterator:def __init__(self, num):self.num num # 迭代器长度self.index 0 # 起始值def __iter__(self):return selfdef __next__(self):if self.index self.num:value self.indexself.index 1 # 每次加一return valueelse:raise StopIterationmy_iterator My_Iterator(5) for value in my_iterator:print(value)当然我们可以使用生成器更简洁的实现迭代器 def My_Iterator(num):for i in range(1,num):yield imy_iterator My_Iterator(5) for value in my_iterator:print(value)惰性列表生成器和迭代器实现的是惰性列表是「需要时才计算出值」的列表而不是一次将所有值全部加载到内存而是每次只加载需要的值。这在深度学习加载超大规模数据集时是否有用 带你从零掌握迭代器及构建最简 DataLoader def get_list(num):results []for i in range(1,num):results.append(i)return resultsresults get_list(5) for i in resultsprint(results[i]) ############################ def yield_list(num):for i in range(1,num):yield igenerator yield_list(5) for value in generator:print(value)with 上下文管理器 上下文管理器是一个对象with范围内的动态全局变量定义了运行时的上下文使用with 上下文管理器:来执行在with的管辖范围内可以访问这个上下文管理器在with运行前做一些预处理在with运行结束再做一些后处理。 with context_manager as ctx_m:# 前处理# 随意访问 ctx_m# 后处理最经典的例子就是文件读写不使用上下文管理器时需要我们手动关闭文件。 file open(data.txt, w) file.write(hello) file.close()使用上下文管理器后open(data.txt, w) 返回系统帮我们实现好的File上下文管理器with范围结束时自动执行file.close()关闭文件 with open(data.txt, w) as file:file.write(hello)自定义上下文管理器必须实现__enter__和__exit__这两个方法。进入with时执行__enter__返回ctx_mself退出with时执行__exit__ class Context_Manager:def __init__(self):self.context this is my contextdef __enter__(self):print(Entering context: PreProcess)return selfdef __exit__(self, exc_type, exc_val, exc_tb):print(Exiting context: PostProcess)with Context_Manager() as ctx_m:print(ctx_m.context)Entering context: PreProcess this is my context Exiting context: PostProcess面向对象 抽象和继承定义的类可以作为一个基类抽象类ABC里面的方法是抽象方法用abstractmethod装饰这意味着任何继承自该类都必须重写这个方法。基类的函数没有主体可以将所有可能需要的功能都声明为一个抽象方法或者类。可以先决定一个high-level的实现然后定义一个新的类继承它详细地处理每个部分。 from abc import ABC, abstractmethod class BaseModel(ABC):def __init__(self, cfg):self.cfg cfgabstractmethoddef train(self):passabstractmethoddef test(self):passclass YourNet(BaseModel):def __init__(self, cfg):super(YourNet, self).__init__(cfg)# create modeldef train(self):# do trainpassdef test(self):# do testpass静态方法和类方法 静态方法staticmethod没有self也没有cls参数在定义类的时候有时候部分函数跟类没关系但是我们又需要用到它。可以在类外面单独定义这个函数但是这样子会使代码变得难理解因此使用静态方法把这个单独的函数搬到类中表示。 class DataLoader:staticmethoddef load_data(file_name):data []with open(file_name, r) as file:for line in file:data.append(line.strip())return datastaticmethoddef save_data(file_name, data):with open(file_name, w) as file:for item in data:file.write(item \n)类方法classmethod 将实际的类作为参数通常用作构造函数 用于创建类的新实例。类方法还有一个用途就是可以对类属性进行修改在用类方法对类属性修改之后通过类对象和实例对象访问都发生了改变。 形式上类方法的第一个参数是类本身我们通常用cls表示通过cls可以调用类方法、类属性和静态方法。类内的其他常规方法第一个参数是self在调用的时候是具体的类的实例。 import json class Config:Configuration class for the applicationdef __init__(self, data, train, model):# 各个部分的超参数self.data dataself.train trainself.model modelclassmethod def from_json(cls, cfg):Create a Config object from a JSON filewith open(cfg, r) as f:data json.load(f)return cls(data[data], data[train], data[model]) 我们从json文件构造一个配置创建一个Config类定义一个类方法from_json 去加载我们的配置信息下图代码第20行起。可以研究一下如何使用类方法和参数cls的。 Mixin 模式 Mix-in常被译为“混入”是一种编程模式在 Python 等面向对象语言中我们把某个功能类FuncMixin写好实现了某种功能单元其他类希望拥有这种功能的时候继承对应的功能类FuncMixin即可通过多继承可以将多个功能组合到子类中。理念上类似装饰器当我们的函数希望使用对应功能的函数时用装饰器即可拥有对应的功能。 定义一个简单的类 class Person:def __init__(self, name, gender, age):self.name nameself.gender genderself.age age我们可以通过调用实例属性的方式来访问 p Person(小陈, 男, 18) print(p.name) # 小陈然后我们定义一个 Mixin 类这个类可以让子类拥有像 字典dict 一样调用属性的功能 class MappingMixin:def __getitem__(self, key):return self.__dict__.get(key)def __setitem__(self, key, value):return self.__dict__.set(key, value)我们将这个 Mixin 加入到 Person 类中 class Person(MappingMixin):def __init__(self, name, gender, age):self.name nameself.gender genderself.age age现在 Person 拥有另一种调用属性方式了 p Person(小陈, 男, 18) print(p[name]) # 小陈 print(p[age]) # 18再定义一个 Mixin 类这个类实现了 __repr__ 方法能自动将属性与值拼接成字符串 class ReprMixin:def __repr__(self):s self.__class__.__name__ (for k, v in self.__dict__.items():if not k.startswith(_):s {}{}, .format(k, v)s s.rstrip(, ) ) # 将最后一个逗号和空格换成括号return s利用 Python 的特性一个类可以继承多个父类 class Person(MappingMixin, ReprMixin):def __init__(self, name, gender, age):self.name nameself.gender genderself.age age这样这个子类混入了两种功能 p Person(小陈, 男, 18) print(p[name]) # 小陈 print(p) # Person(name小陈, gender男, age18)反射机制 核心思想利用字符串去已存在的模块中找到指定的属性或方法找到方法后自动执行——基于字符串的事件驱动。 hasattr(object,attrName)判断该对象是否有指定名字的属性或方法返回值是bool类型setattr(object,attrName,value)给指定的对象添加属性以及属性值getattr(object,attrName)获取对象指定名称的属性或方法返回值是str类型delattr(object,attrName)删除对象指定名称的属性或方法值无返回值 注getattr,hasattr,setattr,delattr对模块的修改都在内存中进行并不会影响模型class定义文件中真实内容下一次重新import创建模型对象时还是原来的类结构。 在正式介绍实际应用场景之前先来看看这样的一个importlib模块。输入多层的模块路径自动生成对象并调用该类的方法。比如notify.email.Emailnotify包下面有模块email模块email(.py文件)中包括了Email类利用该类声明对象并调用其中的send()方法。 # 模块importlib import importlib #notify.email.Email path_str input(请输入包-模块-类的字符串路径) # module_path.class_name module_path,class_name path_str.rsplit(.,maxsplit1) # 1 利用字符串导入模块该方法最小只能到.py文件名即模块 module importlib.import_module(module_path) # from notify import email # 2 利用反射获取类名 cls getattr(module,class_name) # Email、QQ、Wechat # 3 生成类的对象 obj cls() # 4 直接调用send方法 obj.send()一个通用的工具函数就是 def get_obj_from_str(string, reloadFalse):module, cls string.rsplit(., 1)if reload:module_imp importlib.import_module(module)importlib.reload(module_imp)return getattr(importlib.import_module(module, packageNone), cls)常见的应用还有就是对于已经创建好的UNet模型找到对应的CrossAttention类对象然后修改CrossAttention类对象的forward函数 def register_recr(net_, count, place_in_unet):if net_.__class__.__name__ CrossAttention:net_.forward ca_forward(net_, place_in_unet)return count 1elif hasattr(net_, children):for net__ in net_.children():count register_recr(net__, count, place_in_unet)return countcross_att_count 0sub_nets model.unet.named_children()for net in sub_nets:if down in net[0]:cross_att_count register_recr(net[1], 0, down)elif up in net[0]:cross_att_count register_recr(net[1], 0, up)elif mid in net[0]:cross_att_count register_recr(net[1], 0, mid)并发编程 https://www.bilibili.com/video/BV1BY411d7qr/?spm_id_from333.788vd_sourceb2549fdee562c700f2b1f3f49065201b Design Patterns 设计模式前人将一类问题的统一解决办法总结为一种设计模式。当后人遇到相似问题时就可以用对应的模式进行代码设计。 设计模式6个原则 1、开闭原则Open Close Principle接口和抽象类的设计对扩展开放对修改关闭。在程序需要进行拓展的时候不能去修改原有的代码实现一个热插拔的效果。 2、里氏代换原则Liskov Substitution Principle任何基类可以使用的地方派生类一定可以使用。LSP 是继承复用的基石只有当派生类可以替换掉基类且软件单位的功能不受到影响时基类才能真正被复用而派生类也能够在基类的基础上增加新的行为。 3、依赖倒转原则Dependence Inversion Principle针对接口编程(抽象)依赖于抽象类而不依赖于具体类。 4、接口隔离原则Interface Segregation Principle使用多个隔离的专门接口(多继承抽象类)比使用单个总的接口要好。它还有另外一个意思是降低抽象类之间的耦合度强调降低依赖降低耦合。 5、单一职责原则又称最少知道原则Demeter Principle一个类只负责一个职责。一个实体应当尽量少地与其他实体之间发生相互作用使得系统功能模块相对独立。 6、合成复用原则Composite Reuse Principle尽量使用合成/聚合的方式而不是使用继承。 设计模式分类 1、创建型模式关注怎么创建对象。这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 2、结构型模式关注几个类之间如何耦合。这些模式关注对象之间的组合和关系旨在解决如何构建灵活且可复用的类和对象结构。 3、行为型模式关注几个类之间如何交互。这些模式关注对象之间的通信和交互旨在解决对象之间的责任分配和算法的封装。 接下来将讲述各种具体的设计模式我会在比较重要的模式后面打√。 简单工厂模式 工厂模式在创建对象时不会对客户端暴露对象的创建逻辑提供了一种将对象的实例化过程封装在工厂类中的方式通过使用一个共同的工厂类接口来创建不同类型的对象可以将对象的创建与使用代码分离提供一种统一的接口。 角色 工厂角色PaymentFactory抽象产品Payment具体产品WechatPayment、CashPayment from abc import ABC, abstractmethodclass Payment(ABC):abstractmethoddef pay(self, money):passclass WechatPayment(Payment):def __init__(self, use_cardFalse):super().__init__()self.use_card use_carddef pay(self, money):if self.use_card: print(fUsing WeChat BankCard Pay {money}) # 用微信的银行卡支付else: print(fUsing WeChat LingQian Pay {money}) # 用微信零钱支付class CashPayment(Payment):def pay(self, money):print(fUsing Cash Paying {money})class PaymentFactory:def create_payment(self, payment_type):if payment_type wechat_lingqian:return WechatPayment(use_cardFalse)elif payment_type wechat_bankcard:return WechatPayment(use_cardTrue)elif payment_type cash:return CashPayment()else:raise ValueError(Unknown payment type)factory PaymentFactory() payment factory.create_payment(wechat_lingqian) payment.pay(100 优点 1、隐藏了对象创建的细节一个调用者想创建一个对象调用者只关心产品的接口。 2、扩展性高如果想增加一个产品只要扩展一个工厂类就可以客户端不需要修改代码。 缺点1、违反了单一职责原则将创建逻辑集中到一个工厂中。2、违法开闭原则每次增加一个产品时都需要增加一个具体类和对象实现工厂使得系统中类的个数成倍增加在一定程度上增加了系统的复杂度同时也增加了系统具体类的依赖。这并不是什么好事。 工厂模式 √ 为了避免简单工厂模式中工厂类集中所有对象创建的逻辑我们引入抽象工厂类对起进行解耦便于修改和维护增加产品时只需要继承抽象工厂创建新的工厂类即可而不需要修改抽象工厂类。 主要解决主要解决接口选择的问题。 何时使用我们明确地计划不同条件下创建不同实例时。 角色 抽象产品Abstract Product定义了产品的共同接口或抽象类。它可以是具体产品类的父类或接口规定了产品对象的共同方法。具体产品Concrete Product实现了抽象产品接口定义了具体产品的特定行为和属性。抽象工厂Abstract Factory声明了创建产品的抽象方法可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。具体工厂Concrete Factory实现了抽象工厂接口负责实际创建具体产品的对象。 from abc import ABC, abstractmethodclass Payment(ABC):abstractmethoddef pay(self, money):passclass WechatPayment(Payment):def __init__(self, use_cardFalse):super().__init__()self.use_card use_carddef pay(self, money):if self.use_card: print(fUsing WeChat BankCard Pay {money}) # 用微信的银行卡支付else: print(fUsing WeChat LingQian Pay {money}) # 用微信零钱支付class CashPayment(Payment):def pay(self, money):print(fUsing Cash Paying {money})class PaymentFactory(ABC):abstractmethoddef create_payment(self):passclass WechatLingQianPaymentFactory(PaymentFactory):def create_payment(self):return WechatPayment(use_cardFalse)class WechatBankCardPaymentFactory(PaymentFactory):def create_payment(self):return WechatPayment(use_cardTrue)class CashPaymentFactory(PaymentFactory):def create_payment(self):return CashPayment()factory WechatLingQianPaymentFactory() payment factory.create_payment() payment.pay(100)优点1每个具体产品都对应一个具体工厂类不需要修改工厂类代码。2隐藏了对象创建的实现细节。 缺点每增加一个具体产品类就必须增加一个想应的具体工厂类代码太多。 抽象工厂模式 定义一个工厂类接口让工厂子类创建一系列相关或相互依赖的对象。相比工厂模式抽象工厂模式中每个具体的工厂都生产一套产品。如生产一部手机需要手机壳、CPU、操作系统三类对象进行组装其中每类对象都有不同的种类。对每个具体工厂分别生产一部手机所需要的三个对象。 角色 抽象工厂Abstract Factory声明了一组用于创建产品对象的方法每个方法对应一种产品类型。抽象工厂可以是接口或抽象类。具体工厂Concrete Factory实现了抽象工厂接口负责创建具体产品对象的实例。抽象产品Abstract Product定义了一组产品对象的共同接口或抽象类描述了产品对象的公共方法。具体产品Concrete Product实现了抽象产品接口定义了具体产品的特定行为和属性。 from abc import ABC, abstractmethod# 抽象产品 class PhoneShell(ABC):abstractmethoddef show_shell(self):passclass CPU(ABC):abstractmethoddef show_cpu(self):passclass OS(ABC):abstractmethoddef show_os(self):pass# 抽象工厂 class PhoneFactory(ABC):abstractmethoddef make_shell(self):passabstractmethoddef make_cpu(self):passabstractmethoddef make_os(self):pass# 具体产品 class SmallShell(PhoneShell):def show_shell(self):print(small shell)class BigShell(PhoneShell):def show_shell(self):print(big shell)class HighEndCPU(CPU):def show_cpu(self):print(high end cpu)class LowEndCPU(CPU):def show_cpu(self):print(low end cpu)class AndroidOS(OS):def show_os(self):print(android os)class IOSOS(OS):def show_os(self):print(ios os)# 具体工厂 class HuaWei_S_H(PhoneFactory):def make_shell(self):return SmallShell()def make_cpu(self):return HighEndCPU()def make_os(self):return AndroidOS()class Apple_B_L(PhoneFactory):def make_shell(self):return BigShell()def make_cpu(self):return LowEndCPU()def make_os(self):return IOSOS()# 客户端 class Phone:def __init__(self, shell, cpu, os):self.shell shellself.cpu cpuself.os osdef show_info(self):print(phone info:)self.shell.show_shell()self.cpu.show_cpu()self.os.show_os()def make_phone(phone_factory):return Phone(phone_factory.make_shell(), phone_factory.make_cpu(), phone_factory.make_os())p make_phone(HuaWei_S_H()) p.show_info()优点当一个产品族中的多个对象被设计成一起工作时它能保证客户端始终只使用同一个产品族中的对象。 缺点产品族扩展非常困难要增加一个系列的某一产品既要在抽象的 Creator 里加代码又要在具体的里面加代码。如Phone的组件中加一个GPU既要修改Phone抽象工厂和抽象产品代码又要增加GPU具体产品代码。 建造者模式 √ 建造者模式Builder Pattern使用多个简单的对象一步一步构建成一个复杂的对象。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。 主要解决主要解决在软件系统中有时候面临着复杂对象的创建工作其通常由各个部分的子对象用一定的算法构成由于需求的变化这个复杂对象的各个部分经常面临着剧烈的变化但是将它们组合在一起的算法却相对稳定。 何时使用一些基本部件不会变而其组合经常变化的时候。 角色 复杂类由多个组件构成的复杂类。抽象Builder使用抽象方法定义复杂类每个组件的build抽象方法。具体Builder具体实现抽象build方法从而可以构造多种类型的复杂类的具体类。导演Director定义Builder中每个组件build方法的执行顺序。 from abc import ABC, abstractmethod# 复杂类 class Player:def __init__(self, faceNone, bodyNone, armNone, legNone):self.face faceself.body bodyself.arm armself.leg legdef __str__(self):return fPlayer(face{self.face}, body{self.body}, arm{self.arm}, leg{self.leg})# 抽象Builder class PlayerBuilder(ABC):abstractmethoddef build_face(self):passabstractmethoddef build_body(self):passabstractmethoddef build_arm(self):passabstractmethoddef build_leg(self):pass# 具体Builder class SexyGirlBuilder(PlayerBuilder):def __init__(self):self.player Player()def build_face(self):self.player.face Beautiful facedef build_body(self):self.player.body Sexy bodydef build_arm(self):self.player.arm Slim armdef build_leg(self):self.player.leg Long legclass MonsterBuilder(PlayerBuilder):def __init__(self):self.player Player()def build_face(self):self.player.face Monster facedef build_body(self):self.player.body Monster bodydef build_arm(self):self.player.arm Monster armdef build_leg(self):self.player.leg Monster leg# Director class PlayerDirector: # 定义build顺序def build_player(self, builder): builder.build_face()builder.build_body()builder.build_arm()builder.build_leg()return builder.player# 客户端 builder SexyGirlBuilder() director PlayerDirector() sexy_girl director.build_player(builder) print(sexy_girl.__str__())优点1分离构建过程和表示使得构建过程更加灵活可以构建不同的表示。2可以更好地控制构建过程隐藏具体构建细节。3代码复用性高可以在不同的构建过程中重复使用相同的建造者。 缺点1如果产品的属性较少建造者模式可能会导致代码冗余。2建造者模式增加了系统的类和对象数量。 单例模式 √ 单例模式Singleton Pattern确保一个单例类只创建一个实例并给所有其他对象提供这一实例的全局访问点来访问该实例。 主要解决一个全局使用的类频繁地创建与销毁。 何时使用当您想控制实例数目节省系统资源的时候。如日志对象我们希望是单例的不希望每个进程都创建一个然后同时对log文件进行写入。 在讲单例模式前我们先来回顾一下类的实例化 class MyClass:def __init__(self, num):self.num nummy_class_instance MyClass(10)Python类默认继承Object类在使用MyClass(10)后先用 __new__方法创建这个类的实例然后__init__是在类实例创建之后用于初始化实例的属性值。 角色 Singleton单例基类重写Obect类的__new__方法控制cls子类创建实例时检查是否已经有已经创建的实例。 class Singleton:def __new__(cls, *args, **kwargs): if not hasattr(cls, _instance):cls._instance super(Singleton, cls).__new__(cls)return cls._instanceclass MyClass(Singleton):def __init__(self, num):self.num numa MyClass(10) print(a.num) b MyClass(20) print(b.num)优点1、在内存里只有一个实例减少了内存的开销尤其是频繁的创建和销毁实例比如管理学院首页页面缓存。2、避免对资源的多重占用比如写文件操作。 缺点没有接口不能继承与单一职责原则冲突一个类应该只关心内部逻辑而不关心外面怎么样来实例化。 适配器模式 √ 当2个类不能一起工作时引入适配器类作为两个不兼容的接口之间的桥梁。 主要解决主要解决在软件系统中常常要将一些现存的对象放到新的环境中而新环境要求的接口是现对象不能满足的。 何时使用 系统需要使用现有的类而此类的接口不符合系统的需要。 还是支付类的例子新加一个不适配Payment的支付类ZFBPayment不能之间对ZFBPayment的对象调用.pay方法而且**cost方法在其他很多模块中已经使用修改成本很大**。 只有一个类接口有问题时多继承我们可以重写一个NewZFBPayment类继承两种接口(pay和cost)然后统一接口 from abc import ABC, abstractmethodclass Payment(ABC):abstractmethoddef pay(self, money):passclass WechatPayment(Payment):def pay(self, money):print(fUsing WeChat Pay {money})class CashPayment(Payment):def pay(self, money):print(fUsing Cash Paying {money})class ZFBPayment: # 新加一个不适配Payment的支付类而且cost方法在其他很多模块中已经使用修改成本很大def cost(self, amount):print(fUsing ZFB Pay {amount})class NewZFBPayment(Payment, ZFBPayment): # 适配器def pay(self, money):self.cost(amount money)zfb_pay NewZFBPayment() zfb_pay.pay(100)如果有很多类接口都不匹配组合对每个不匹配的类都重写一个适配器类太繁琐可以定义一个总的适配器PaymentAdapeter传入任意的类在PaymentAdapeter中统一接口 from abc import ABC, abstractmethodclass Payment(ABC):abstractmethoddef pay(self, money):passclass WechatPayment(Payment):def pay(self, money):print(fUsing WeChat Pay {money})class CashPayment(Payment):def pay(self, money):print(fUsing Cash Paying {money})class ZFBPayment:def cost(self, amount):print(fUsing ZFB Pay {amount})class ApplePayment:def cost(self, amount):print(fUsing Apple Pay {amount})class PaymentAdapeter(Payment):def __init__(self, payment):self.payment paymentdef pay(self, money):self.payment.cost(money)apple_pay ApplePayment() adapter PaymentAdapeter(apple_pay) adapter.pay(100)zfb_pay ZFBPayment() adapter PaymentAdapeter(zfb_pay) adapter.pay(100)优点 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。 缺点 1、过多地使用适配器会让系统非常零乱不易整体进行把握。比如明明看到调用的是 A 接口其实内部被适配成了 B 接口的实现一个系统如果太多出现这种情况无异于一场灾难。因此如果不是很有必要可以不使用适配器而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类所以至多只能适配一个适配者类而且目标类必须是抽象类。 观察者模式 √ 观察者模式Observer Pattern定义了一种一对多的依赖关系当一个对象的(发布者)状态发生改变时其所有依赖者(订阅者)都会收到通知并自动更新。也叫“发布-订阅“模式。 主要解决一个对象状态改变给其他对象通知的问题而且要考虑到易用和低耦合保证高度的协作。 何时使用一个对象目标对象的状态发生改变所有的依赖对象观察者对象都将得到通知进行广播通知。 角色 抽象主题Subject具体主题ConcreteSubject发布者抽象观察者Observer具体观察者ConcreteObserver订阅者 from abc import ABC, abstractmethod# 抽象订阅者 class Observer(ABC):abstractmethoddef update(self, subject, arg): # Update observerpass# 抽象发布者 class Subject:def __init__(self):self._observers [] # Save all observersdef register(self, observer):self._observers.append(observer) # Register observer to subscribe subjectdef remove(self, observer):self._observers.remove(observer) # observer cancel subscribedef notify(self, arg):for observer in self._observers: # Notify all observersobserver.update(self, arg) # 传入subject和arg# 具体订阅者 class ConcreteObserver(Observer):def update(self, subject, arg):print(f{self.__class__.__name__ }: {arg})# 具体发布者 class ConcreteSubject(Subject):def __init__(self):super().__init__()self._state 0 # 私有属性property # 属性装饰器def state(self): # 读subject.state自动返回subject._statereturn self._statestate.setterdef state(self, value): # 写subject.state自动修改subject._stateself._state valueself.notify(self._state) # 同时自动通知所有订阅者subject ConcreteSubject() observer1 ConcreteObserver() observer2 ConcreteObserver() subject.register(observer1) subject.register(observer2) subject.state 10 subject.state 20优点 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。 缺点 1、如果一个被观察者对象有很多的直接和间接的观察者的话将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话观察目标会触发它们之间进行循环调用可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的而仅仅只是知道观察目标发生了变化。 策略模式 √ 策略模式定义了一系列算法或策略并将每个算法封装在独立的类中使得它们可以互相替换。通过使用策略模式可以在运行时根据需要选择不同的算法而不需要修改客户端代码。创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。 主要解决在有多种算法相似的情况下使用 if…else 所带来的复杂和难以维护。 何时使用一个系统有许多许多类而区分它们的只是他们直接的行为。 角色 抽象策略Strategy具体策略ConcreteStrategy上下文Context from abc import ABC, abstractmethod# 抽象策略 class Strategy(ABC):abstractmethoddef execute(self, data):pass# 具体策略 class FastStrategy(Strategy):def execute(self, data):print(fFast strategy using data {data})class SlowStrategy(Strategy):def execute(self, data):print(fSlow strategy using data {data})# 上下文 class Context:def __init__(self, strategy, data):self._strategy strategyself.data datadef set_strategy(self, strategy): # 切换策略self._strategy strategydef do_strategy(self): # 执行策略self._strategy.execute(self.data)# 客户端 context Context(FastStrategy(), 666) context.do_strategy() context.set_strategy(SlowStrategy()) context.do_strategy()优点 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。 缺点 1、策略类会增多。 2、所有策略类都需要对外暴露。 模板方法模式 √ 模板模式Template Pattern定义一个操作中的算法的骨架而将一些具体步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构(如下面的run)即可重定义该算法的某些特定步骤。子类可以按需要重写方法实现但调用将以抽象类中定义的方式进行。 主要解决一些方法通用却在每一个子类都重新写了这一方法。 何时使用有一些通用的方法。 角色 抽象类 (AbstractClass定义抽象的原子操作(钩子操作) ; 实现一个模板方法作为算法的骨架。同时抽象模板类已经把每个抽象方法的执行顺序定义好了。具体类 (ConcreteClass)实现原子操作。 例子窗口显示类Window抽象类定义好算法在执行流程与结构run子类不可该改变但抽象的原子操作没有定义。子类实现抽象的原子操作。 from abc import ABC, abstractmethod from time import sleep # 抽象模板类 class Window(ABC):abstractmethoddef start(self):passabstractmethoddef repaint(self):passabstractmethoddef stop(self):passdef run(self): # 模板类已经把每个抽象方法的执行顺序定义好了self.start()while True:try:self.repaint()sleep(1)except KeyboardInterrupt:breakself.stop()class Window1(Window):def __init__(self, msg):self.msg msgdef start(self):print(Window1 started)def repaint(self):print(fWindow1 repainted {self.msg})def stop(self):print(Window1 stopped)win Window1(Hello) win.run()优点 1、封装不变部分扩展可变部分。 2、提取公共代码便于维护。 3、行为由父类控制子类实现。 缺点每一个不同的实现都需要一个子类来实现导致类的个数增加使得系统更加庞大。 Clean Code 优质代码的通用特性 3个核心概念 可扩展性在功能演进时是否容易维护还是要修改大量代码、甚至是重构可读性是否很容易让人读懂并且没有歧义Make code easy to understanding简洁性代码的逻辑、实现是否精简 只要在写代码的时候大脑里时刻想着这3个概念就能联想到这本书里的诸多方法就能做到“下笔如有神了”Write your code as a good story! 写好代码就像写好文章一样好的代码、文章都需要通过符合一定的规范让别人很轻易地读懂。 Clean Code的关键命名、代码结构、注释、函数、控制结构、错误处理、类与数据结构 命名 Naming 3种命名格式 小驼峰式命名法第一个单词首字母小写后面其他单词首字母大写如userData。JAVA的变量和函数使用。大驼峰式命名法每个单词的第一个字母都要大写如UserData。Python和JAVA的类名使用。下划线命名法单词与单词之间通过下划线连接即可如user_data。Python的变量和函数使用。 命名的核心思想①Names should be meaningful 好的名字是有明确意义的应该向读者反映出一个变量或者一个函数代表什么而不必 dive into 去查看内部是实现细节应避免歧义的缩写。②Dont Include Redundant Information In Names !并不是名字越长越好清晰没有歧义即可如user_with_name_and_age我们并不需要把一个对象的所有属性等冗余信息都体现在名字里。③Avoid Slang, Unclear Abbreviations Disinformation !避免使用俗语语义不明的缩写以及误导性的名字。 变量名常量名属性名本质是数据容器因为我们想强调的是存储在其中的数据内容使用名词(组合)或is形容词短语。一般用下划线命名法。 # 下划线命名法 user_data [...] is_valid True总结数据型变量可以直接使用名词(组合)布尔型变量通常使用is 形容词短语来指示True/False。 例子 函数名本质是命令指令因为我们想强调的是函数执行的功能使用动词(名词短语)或is形容词短语。一般用下划线命名法。 # 下划线命名法 def send_data(): def is_input_valid():总结操作型函数可以直接使用动词(名词短语)判断型函数则使用is 形容词短语。 例子 类名本质是创建一个事物因为我们想强调的是事物的类型使用名词或多个名词组合短语。一般用大驼峰命名法。 # 大驼峰命名法 class User: class RequestBody:总结随着类从抽象到不断派生继承我们可以逐渐将类名设置的越来越具体。 例子 本节测试题博客发表将下面的dirty code中的对象的坏的名字重命名为好的名字转换为clean code。 dirty code from datetime import datetimeclass Entity:def __init__(self, title, description, ymdhm):self.title titleself.description descriptionself.ymdhm ymdhmdef output(item):print(Title: item.title)print(Description: item.description)print(Published: item.ymdhm)summary Clean Code Is Great! desc Actually, writing Clean Code can be pretty fun. You\ll see new_data datetime.now() publish new_data.strftime(%Y-%m-%d %H:%M) item Entity(summary, desc, publish) output(item)clean_code: from datetime import datetimeclass BlogPost:def __init__(self, title, description, publish_time):self.title titleself.description descriptionself.publish_time publish_timedef print(self):print(Title: self.title)print(Description: self.description)print(Published: self.publish_time)title Clean Code Is Great! description Actually, writing Clean Code can be pretty fun. You\ll see now_time datetime.now() publish_time now_time.strftime(%Y-%m-%d %H:%M)blog_post BlogPost(title, description, publish_time) blog_post.print()总结 注释 Comments 核心思想① Avoid Comments !因为大多数注释都是冗余的命名清晰的代码完全可以像文章一样阅读除了某些特殊情况应该尽量避免注释预期添加注释不如优化代码命名Naming方式。② Avoid Divider or Block Marks !当你很想使用####### Do Something ######这种分隔符或块标记来显著地给代码分块时说明一个文件中写了太多代码了你应该考虑将代码拆开而不是添加这些冗余的分隔符。③ Avoid Commented-Out Code !永远不会使用的代码应该大胆删除而不是将其永久性的注释掉。 法律信息Legal Information在文件顶部的Legal Information通常是被要求具有的而且在文件顶部不影响代码的阅读。 # (c) Maximilian Schwarzmuller Academind GmbH # Created in 2020代码表意不清当我们使用正则表达式或者计算数学公式时无法使用合适的命名来清晰的表达代码的含义可以通过注释来进一步解释。 # compute epsilon eps self.model(x_t, t - 1)# compute x_0 for first item x_0 self.predict_xstart_from_eps(x_t, t - 1, eps) if self.clip_denoised:x_0 torch.clamp(x_0, min-1., max1.) # 裁剪梯度# compute sigma for thrid item sigma_t self.ddim_eta * torch.sqrt((1 - alpha_cumprod_t_prev) / (1 - alpha_cumprod_t) * (1 - alpha_cumprod_t / alpha_cumprod_t_prev))# compute pred_dir_xt for second item pred_dir_xt torch.sqrt(1 - alpha_cumprod_t_prev - sigma_t ** 2) * epsx_prev torch.sqrt(alpha_cumprod_t_prev) * x_0 pred_dir_xt sigma_t ** 2 * torch.randn_like(x_t)警告信息Warning提示如何正确的使用代码否则可能会报错。 # only run in fp32, not suppurt fp16 weight_dtype fp32待做TODO在多人协作编写的大型项目中你需要在未完成的部分写上TODO note表示待办事项方便其他合作者一起编写待实现的功能在TODO中会简略说明。 def register_attention(model):# TODO: Needs to be implemented register attention for UNet待维护FIXME如果代码中有该标识说明代码存在缺陷需要修复甚至代码是错误的不能工作如何修正会在FIXME中简略说明。 # FIXME: Dont sure deepcopy is necessary latents copy.deepcopy(latents.detch())代码格式化 Code Formatting 核心思想代码的格式应该像文章一样可以从上倒下的流畅阅读该缩进的缩进该分段的分段根据语义没有太多的跳跃。①Splitting files with multiple concepts into multiple files对于一个多大的文件一个文件定义了很多类将代码按照语义划分到不同的文件中尽量保证每个文件只用一个类或者一个种类的几个类。 垂直格式化①不同语义的代码块用行间隔blink lines而相似语义的代码不应该被插入行间隔。②相关联的代码应该彼此靠近比如一个类内的不同方法彼此调用我们应该按照他们的先后执行顺序与相互调用关系来调整每个类方法的位置尽量让先调用的方法在上面紧接着被调用的方法。这样可以避免混乱的跳转。 class Excute_Order:def last(self):print(Last)def next(self):print(Next)self.last()def start(self):print(Start)self.next()excute_order Excute_Order() excute_order.start()水平格式化①缩进Indendation是Python必须的语法。②代码间的空格可以使代码看起来更加简洁清晰。③保证每行代码不要太长可以使用换行符\来续行。 sigma_t self.ddim_eta * torch.sqrt( \(1 - alpha_cumprod_t_prev) / \(1 - alpha_cumprod_t) * \(1 - alpha_cumprod_t / alpha_cumprod_t_prev))总结 函数 Functions 核心思想①Calling and Working of functions should be readable and easy !函数的调用和本体执行部分都应该易于理解和简洁函数调用应当具有可读性函数本体应当简洁不要太长。 输入参数Input ParametersMinimize the number of parameters !函数需要传入的参数越多这个函数就越难以调用函数的可读性就会下降。函数传入的参数个数应当适中一般小于等于3个不能增加用户的理解负担。 例子函数的参数个数与调用的难易程度 2个参数的例子我们应当尽量避免is_error这种bool型的参数可以重构函数将其划分为两个不同功能的函数 from warnings import warn def log(message, is_error):if is_error:warn(message)else:print(message)log(Hello, World!, False) log(This is an error message, True)def log(message):print(message) def log_error(error_message):warn(error_message)log(Hello, World!) log_error(This is an error message)多个参数的例子当我们不得不传入多个参数时不要一个一个的无结构化传入(参数的名称和顺序会变得混乱)可以将多个参数重构打包为一个结构化容器参数例如一个对象objec或一个字典dict。 class User:def __init__(self, name, age, sex):self.name nameself.age ageself.sex sexuser User(张三, 30, 男)class User:def __init__(self, user_data:dict):self.name user_data[name]self.age user_data[age]self.sex user_data[sex]user User({name: 张三, age: 30, sex: 男})同时也应当注意*args和**kwargs的使用*args将无名参数value打包为一个tuple元组使用args[i]访问**kwargs将有名参数keyvalue打包为一个dict字典使用kwargs[key] class User:def __init__(self, *args, **kwargs):self.user_num args[0]self.name kwargs[name]self.age kwargs[age]self.gender kwargs[gender]self.address kwargs[address]user User(1, nameJohn, age20, genderMale, addressNew York)输出参数Oputut Parameters我们应该避免输出不必要的参数。如下给user添加id的操作。 抽象程度Abstract Level抽象程度分为heigh-level 和 low-level 的代码heigh-level代码是调用我们自己编写的代码完成一件事情如email.include() low-level 的代码是直接使用原始代码或三方库的API完成一件事情如isEmail(mail)。Dont Mix Levels of Abstract !在一个函数中不要多种层次的代码混用尽量将low-level的操作封装为high-level的代码然后在函数中进行调用这样我们就可以像阅读文章一样读代码了清晰明了。 heigh-level 和 low-level 的例子 函数长度LengthFunction should be small !函数体应当简洁如果过长需要划分为多个函数。当你想用注释# 1. do something 、# 2. do something来标记或者划分代码时说明你的函数过长了可以按照不同功能的代码块重新组建子函数子函数是相当短小简洁的每个子函数只完成概念层面的一件事情封装好的子函数可以在很多地方重用。 划分函数的两个法则①当我们可以提取功能相似的代码将其封装为单个操作时。②因为抽象程度Abstract Level不一致某部分代码比周围的代码需要更多注释时。 本节测试题创建数据库用户优化dirty code的函数转换为clean code。 dirty code from warnings import warndatabase []def create_database_user(email, password):if ((not email) or ( not in email) or (not password)): # low levelwarn(Invalid email or password) # low leveluser {email: email, password: password} # low leveldatabase.append(user) # low levelcreate_database_user(testtest.com, test)clean code from warnings import warn database []def is_input_invalid(email, password):if (not email) or ( not in email) or (not password):warn(Invalid email or password)def save_user(email, password):user {email: email, password: password}database.append(user)def create_database_user(email, password):is_input_invalid(email, password)save_user(email, password)create_database_user(testtest.com, test)可重用性ReusablilityDRY: Dont repeat yourself !不要重复要重用相同的代码复制粘贴在很多地方重用会导致修改时要改很多地方而将简单的功能封装为一个子函数就可以在任意的地方调用。 dirty code from warnings import warn database []def is_input_invalid(email, password):if (not email) or ( not in email) or (not password) or (password.strip() ):warn(Invalid email or password)def create_database_user(email, password):is_input_invalid(email, password)# TODO: save user datadef create_support_channel(email):if (not email) or ( not in email):warn(Invalid email)# TODO: save support channelclean code from warnings import warn database []def is_email_invalid(email):if (not email) or ( not in email):warn(Invalid email)def is_password_invalid(password):if (not password) or (password.strip() ):warn(Invalid password)def create_database_user(email, password):is_email_invalid(email)is_password_invalid(password)# TODO: save user datadef create_support_channel(email):is_email_invalid(email)# TODO: save support channel合理划分Split Functions Reasonably !过度细化的划分函数会造成碎片化当我们想要划分函数时思考划分后是否会增加代码的可读性同时保证代码足够简洁。 例如对上述例子中的warn函数再封装一层log_error是没有必要的因为原始代码已经足够简洁再封装一层做的也只是ReName重命名操作 warn(Invalid email)def log_error(message):warn(message)单元测试Unit Testing当我们写完一个函数时可以模拟函数输入来进行单元测试。如果我们发现我们不能很简单的测试我们的函数说明这个函数可能需要被划分为多个。 总结 控制结构 Control Structures 核心思想①Avoid deeply nested control structure !我们应当避免深度嵌套的控制结果。 用警卫Guard进行早退出利用 if 构造Guard当Guard条件不满足的时候直接return进行早退出这样可以避免不必要的嵌套同时减少不必要的代码运行。 dirty code def deep_if(x):if x 10:if x 20:if x 30:if x 40:print(x 40)else:print(30 x 40)else:print(20 x 30)else:print(10 x 20)else:print(x 10)print(deep_if(50))clean code def deep_if_guard(x):if x 10:print(x 10)returnif x 20:print(10 x 20)returnif x 30:print(20 x 30)returnprint(x 30)print(deep_if_guard(50))使用正向判断Postive Checks多用if is_empty()而不是if is_not_empty()因为正向的判断函数更加易于理解。 def login(user_id, password):if (not user_id is ) and (not password is ):# do loginpassreturn Trueelse:return Falsedef is_empty(str):return True if str is else Falsedef login(user_id, password):if is_empty(user_id) or is_empty(password): # guardreturn False # early return# do loginreturn True 错误处理Errors HandlingWork with error !大胆拥抱Errors要学会合理地使用try: ... \ except Exception as e: ... 进行错误处理而不是一味的嵌套if-else合理的Throwing和Handling errors可以替代无用的if让我们专注函数的功能实现。 from warnings import warn try:print(1 // 0) except Exception as e:print(e) # 不触发异常正常执行只打印Error信息print(end print)warn(e) # 不触发异常只出发警告UserWarningprint(end warn)raise e # 触发异常终止运行integer division or modulo by zero end print end warn C:\Users\Lenovo\AppData\Local\Temp\ipykernel_21700\687186242.py:7: UserWarning: ewarn(e) # 不触发异常只出发警告warn --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) Cell In[13], line 97 warn(e) # 不触发异常只出发警告warn8 print(end warn) ---- 9 raise e # 触发异常终端运行10 print(end error)Cell In[13], line 31 from warnings import warn2 try: ---- 3 print(1 // 0)4 except Exception as e:5 print(e) # 不触发异常正常执行只打印error信息ZeroDivisionError: integer division or modulo by zero使用工厂Factory和多态Polymorphism工厂函数用于创建一类产品是一种设计模式的思维多态是类与对象中相同函数可以传入不同的参数。两者都在说我们使用相同的函数接口在不同的环境或者条件下创建不同的对象或调用不同的功能。 dirty code def process_by_method(transaction):if transaction.method CREDIT_CARD:processCreditCardTransaction(transaction)elif transaction.method PAYPAL:processPaypalTransaction(transaction)elif transaction.method CASH:processCashTransaction(transaction)clean code def get_transaction_processor(transaction):if transaction.method CREDIT_CARD:return processCreditCardTransactionelif transaction.method PAYPAL:return processPaypalTransactionelif transaction.method CASH:return processCashTransactiondef process_by_method(transaction):processor get_transaction_processor(transaction)processor(transaction)总结 类与对象 ClassObject 总结 设计模式 Design Patterns 鉴于Python的特征有些经典设计模式在Python中并不需要以不可见的方式提供了这些模式Python并不适用工厂模式而其他设计模式也不应强行在解决方案中使用设计模式而应通过演进、重构和改善解决方案让设计模式浮现出来。 不用 工厂模式由于Python在类、函数和自定义对象没有什么不同都可以作为参数进行传递、被赋值只需要定义一个创建对象的函数通过参数将要创建的对象所属的类传递给它。 Python比较适用的模式 单态模式可以有很多属于常规对象的实例而无须关心它们是否是单例。其优点是这些对象包含的信息将以完全透明的方式同步。适配器模式也被称为包装器模式解决适应多个不兼容对象的接口问题装饰器模式无须使用继承就能动态地扩展对象的功能门面模式适用于需要交互的多个对象之间存在多对多关系的情形不仅适用于类和对象还适用于包职责链模式适用合适的事件对象来处理数据通过封装在类中的方法来分配职责模板方法模式在类层次结构中定义某种行为这个层次结构中的所有类都使用相同的模板很容易保留多态性命令模式将请求执行操作和实际执行操作的时间分开将客户端发出的请求与接收方分开状态模式使域问题中的概念成为一个显式的对象而不仅仅是一个边值空对象模式函数和方法必须返回类型始终一致的对象 Clean Architecture 关注点分离组件尽可能小高内聚和低耦合让不同的组件承担不同的职责 找出可能被重用多次的通用逻辑并将其放在一个Python包中采用微服务架构将应用程序分成多个小型服务 将不同职责分配给不同的服务将其封装在可被众多其他服务调用的微服务中抽象代码必须有很强的表达力并使用正确的抽象来揭示核心问题的解决方案
http://www.zqtcl.cn/news/638780/

相关文章:

  • 设计师35岁后的出路嘉兴做网站优化的公司
  • 网站首页包含的内容网站网站注册
  • 企业网站改版建议北京市在建工程项目查询
  • 广州通和通信建设有限公司网站myeclipse怎么做网页
  • 最好的做网站公司有哪些泰安人才网官网登录
  • 怎么用wordpress修改网站源码辽宁省营商环境建设局网站
  • 做网站数据库怎么做wordpress video主题
  • 田园综合体建设网站梧州网站建设有哪些
  • 公司做网站的流程茂名网站建设公司
  • 徐州专业网站建设公司wordpress tag找不到
  • 网站互动推广织梦网站主页代码在后台怎么改
  • 福永自适应网站建设微信小程序功能开发
  • 制作一个动态企业网站狠狠做最新网站
  • 手机建立一个免费网站网页设计师培训方法
  • 广州工信部网站查询wordpress mysql类
  • 销售网站内容设计书籍管理网站建设需求文档
  • 韩国网站如何切换中文域名如何备案教程
  • 网站维护的基本概念二维码生成器使用方法
  • 公司网站建设模块简介搭建自己的网站需要什么
  • 想做个网站怎么做给国外网站做流量
  • 长春建站培训班免备案虚拟空间
  • 做面包的公司网站alexa世界排名查询
  • 网站备案后下一步做什么263邮箱注册
  • 燕郊网站制作廊坊网站制作网站
  • 开网站建设网站如何做excel预览
  • p2p网站建设方案电商企业有哪些
  • 建设农场网站天元建设集团有限公司法定代表人
  • 论坛网站建设价格百度广告官网
  • 网站开发有哪些语言ps做登录网站
  • 网站怎么做百度关键字搜索国外服务器做网站不能访问