设计模式以及python中的函数是第一类对象

我本来是想学习一下python中的设计模式,后来发现这可能是一个"伪命题",有很多书都讲解java或C++中的设计模式。但很难找到一本python

为何大量设计模式在动态语言中不适用?
A. 有一种比惯用法(idiom)抽象层次高那么一点点的东西,我们给这种东西起个名字,叫设计模式。
B. 这里有一套总结设计模式的套路,我们可以拿着这个套路去审视各种开发活动,把找到的共性的东西按照这个模板总结出来。
C. 面向对象开发,组合优于继承。
这几个东西,放到什么面向对象语言里都适用,只看前两点的话,随便什么编程活动(甚至编程之外的其他活动)都适用。
23个模式,是“怕你们看不明白,这里把前面那三点应用于C++或Java这类静态面向对象语言上,看看能找到哪些设计模式。”

比如说,静态语言讲究“对修改封闭,对扩展开放”(打了包分发的东西根本就不能改变其执行逻辑),某些动态语言根本不用管,metaClass,prototype随便改。但允许这样改是不是一定好,未必,得看场景。再比如,静态语言讲究面向协议编程,你要传值就必须保证继承接口或父类来承诺协议。某些动态语言根本不用管,只要传个东西进去,在运行过程中不管三七二十一直接拿来调方法试试,如果传进去的东西又刚好带了个叫这个名字的方法,就调用成功了,否则就抛个运行期错误。好玩的是,人们还给这种“根本不用管typing的玩法”起了个名字叫“Duck Typing”(“我的规矩就是没规矩,这种没规矩的规矩叫Duck规矩”)。允许这样玩是不是一定好,也未必,得看场景。

设计模式是为了补足静态语言的缺陷而存在的。解决的都是其他语言里不存在的问题,因为python已经通过添加语言特性解决了大部分这样的缺陷:比如factory method的应用场景基本就是虚构造函数,也就是传一个类名string,构造出该类的对象,这是python天然支持的;还有command,大部分应用场景在python里一个lambda就搞定了。还有一部分是可有可无的,比如singleton、prototype等。
Factory解决的是对象构建和具体实现的耦合,python大多数情况下不用factory是国为本身支持类和函数做为first-class object
比如 Scala 的单例对象,Python 的 kwargs、函数第一性和内省,哪个不是设计模式的替代?

本质上来说,动态语言的多态要比静态语言的多态更纯粹。Java的多态基于interface和类继承,可以说完全是受实现的性能要求(包括编译和运行)的妥协。而动态语言,以Python为例,本质上来说其实只有两个多态的点:callable和getattr
所有的callable,只要能接受相同的参数,就可以通用
所有的对象,只要能通过__getattribute__机制获取到相同的属性,就可以通用

作者:灵剑
链接:https://www.zhihu.com/question/63734103/answer/212353641
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

更重要的是这两条原则不需要任何的声明就能够成立,这样设计上来说要比基于interface和显式定义简洁得多。具体到Python,所有其他特性可以说都是在这个基础上成立的:
类是callable,构造对象就是调用类
类也是对象,构造类是调用metaclass
许多语法特性本质上是获取magic方法(开头和结束)然后调用
获取对象的属性就是调用对象对应类的__getattribute
。__getattribute__可以认为是设计模式里模板模式的体现,它的默认实现有很多注入点,getattr,descriptor,继承,实例方法,类方法,静态方法,dict__等都来自于默认实现。
因为这些特性,原来的所谓设计原则,在动态语言中并不需要特别的设计:
开闭原则:动态语言天生就是对扩展和修改都开放的,不需要任何特别的设计。只要一个新的对象有以前对象相同的属性,它就可以替代以前的对象;只要一个新的callable接受相同的参数,它就可以替代以前的callable。甚至,在必要的时候,以前的实现也可以通过修改类来完全替换掉(Python中一般叫monkey patch)。
里氏代换原则:动态语言中只需要考虑Duck Type,Duck Type都可以互相替代,子类天生是Duck Type,仅此而已。
依赖倒转原则:这个仍然是很重要的,但是在动态语言中,可以认为上层用到了哪些特性,哪些特性就是接口,不需要特意定义。这样不管是增加还是减少都比较容易。要让这个依赖的部分最小化,仍然需要精心设计。
接口隔离原则:同上,用到的才是接口,没有用到的就等于不存在,可以不用实现,可以认为接口天生是最小化的。还可以通过动态检测判断输入是否实现了某个接口,从而自动适应不同特性。
最少知道原则:这个仍然需要设计保证。
合成复用原则:在动态语言中,合成与继承没有本质的区别,无论使用哪一种都不会造成问题。合成可以通过动态属性或者直接复制属性来假装自己是继承关系,继承也可以通过动态属性屏蔽来自父类的接口(比如在Python中可以raise AttributeError)来假装自己不是父类的派生类,甚至还可以不调用父类的__init
()。
可以看出,需要严格考虑得设计原则本来就要少得多,依赖倒转和最少知道两条足矣。因为这些特性,许多以前在Java中需要技巧实现的场景,在Python中都可以用直接的方式实现,比如:类就是自己的Factory实例可以动态增加方法,因此不需要Adaptor或者Decoratorcallable可以单独传递,观察者等模式都可以简化

最主要原因是“函数第一性”(函数是第一类对象)。代码写多了就知道了,说再多没用。

鸭子类型,高阶函数和闭包的特性,让很多模式变得“自然”了。
动态语言的问题在哪里,最明显的问题就是编译期类型检查的缺失使得接口不再被显式定义(最多写到注释里),所以才有那句“代码重构火葬场”,静态语言你把接口改了,看编译器报错,或者看ide红线,就知道哪里该改了,动态语言呢……有些地方的bug甚至运行时测试不到位的话都暴露不出来。

作者:qwerty
链接:https://www.zhihu.com/question/63734103/answer/451333487
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

给出两个Github上设计模式的Python(动态语言),Java(静态语言)实现faif/python-patterns :这是Python版本的iluwatar/java-design-patterns:这是Java版本的以策略模式为例,https://github.com/faif/python-patterns/blob/master/behavioral/strategy.py 这一份Python版本能利用动态语言的特性很方便的处理,而在Java中,我们可能要对interface编写不同的实现。又比如说抽象工厂模式,Python的鸭子类型让你完全不用弄出一堆神奇的借口、继承关系。顺便附上网上找到的与此相关的问答Are there any design patterns that are unnecessary in dynamic languages like Python?希望对您有所帮助0.0

作者:mwish
链接:https://www.zhihu.com/question/63734103/answer/224382958
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Python中包含函数在内的一切皆为对象,函数作为第一类对象,支持赋值给变量,作为参数传递给其它函数,作为其它函数的返回值,支持函数的嵌套,实现了__call__方法的类实例对象也可以当做函数被调用。
如何正确理解Python函数是第一类对象
1,函数可以被赋值

2,函数当成参数传递

3,函数紧跟括号,就执行里面的代码

4,函数不带括号,拿函数的内存地址

有第一类对象,当然也有第二类对象。。

猜你喜欢

转载自blog.csdn.net/kekefen01/article/details/84575562
今日推荐