Python的语言特点

Python有哪些语言特点?可以列出的特点很多,例如,《Python核心编程》第二版列出了十多条特点。本文的三个特点是笔者学习Python的体会,其他特点有体会之后再写,笔者是这样概括的:Python是解释性和编译性结合的动态的面向对象的

解释性和编译性

解释性是指高级语言程序运行的时候依赖于解析器将程序翻译成计算机能理解的低级语言指令,编译性指高级语言运行前先编译成计算机可执行目标低级语言,然后让计算机执行。由于解释型语言在运行过程中仍需解释器逐句翻译而编译型语言只要编译好就可以直接执行而无需再解释,所以相对来说,编译型语言的运行速度快,即性能高。笔者认为,python语言是解释性和编译性混合的。下面多余的展开也就这个意思

       计算机无法识别和执行高级语言,一个高级语言程序在可执行之前先要翻译成一种能被计算机执行的低级语言,这没有贬低的意思,方便而已。而完成这项翻译工作的就是语言处理器,常见的有编译器和解释器。编译器可以将某种高级语言程序翻译成等价的目标语言程序,以被计算机执行。解释器则是在程序文件运行的过程中将其逐句翻译成计算能看懂的指令(二进制码)。由于编译型语言一经编译成目标语言程序,计算机马上可以执行,而解释型在程序运行时还要慢慢的解释每一句给计算机执行,所以,一般来说解释型语言运行速度比编译型的慢;所以把用户输入映射成输出的过程中,由一个编译器产生的机器语言目标程序要比由一个解释器快,也就是编译型的性能好。

       然而为什么还存在解释型呢?当然是由于解释型相对于编译型的一些优点,比如动态(也是缺点,不过笔者认为是优大于缺,而动态和解释性相关,不同观点勿见怪)等。所以为了兼顾性能与开发效率,某些语言自然混合了解释性和编译性。笔者认为,python程序的执行是混合了解释性和编译性的:当程序执行时,python内部(这是一个抽象)先将源程序(即我们编写的程序)编译成“字节码”,这个过程是我们看不见的,即被隐藏起来了,如果python进程在机器上拥有写入权限,那么,它将把程序的字节码保存为一个以.pyc为扩展名的文件(这是一个优化性能的步骤:在下次运行该程序时,如果程序没有变化,那么解释器将直接加载这个文件从而跳过了编译这个步骤以提高速度),然后再发送给虚拟机。字节码一旦发送给虚拟机(PVM),虚拟机便开始逐条执行翻译(如下图)。字节码并不是cpu码(二进制码),所以执行起来相对编译后的二进制码仍然是慢。

不知道为啥图片要刷新才会显示@博客园团队

动态的

  动态,相对于静态,意味着随时可变,意为灵动。也可以称之为动态类型:类型由代码运行过程中自行决定,无需声明强调,而静态类型则是在运行前决定。

变量

  python的变量就是在特定的时间引用一个特定的对象,不同的变量可以引用同一个对象,所以对象地址也是相同的,对象的类型必须是明确的,而所有对象首先必须要知道自己的类型(解析器能够判断,例如对象3知道自己的类型是int),这样的赋值才是有意义,像单纯一句“a=b”这样的变量引用是错误的,因为解释器无法在其作用域中判断出对象b的类型。类型属于对象而不是变量名,所以无需指明变量的类型。

变量的引用和内存管理相关,可以查找引用计数和垃圾回收方面的资料,祥略。

>>> a=1>>> a=0.5>>> a='a'>>> a=[]>>> type(1)>>> type(0.5)>>> type('a')>>> type([])>>> a=b

Traceback (most recent call last):

  File "", line 1,in    a=b

NameError: name 'b'isnotdefined

参数

Python中,参数通过赋值传递,赋值相当于变量的引用,自然,函数和方法中的参数的值和类型也都是动态的,这些参数的值只有在被调用的时候定义,而非在函数内定义。其实,函数定义内的参数是形参,而调用时提供给参数的的值则为实参,参数在函数定义的圆括号对内指定,用逗号分割。当我们调用函数的时候,我们以同样的方式提供值。由于数字不能作为变量,这样的赋值语句是会引发异常的:4=3,如果将函数作为一个参数,即将函数对象被引用,那么该函数就是一个回调函数

>>>defadd(x=1, y=2):                    # 动态参数

    returnx+y>>> add()3>>> add(2,3)5>>> add('a','b')'ab'>>> add([1],[2,3])

[1, 2, 3]>>> 4=3                                      # 数字不能作为变量SyntaxError: can't assign to literal>>>def call_back(i):                        # 回调函数

    print(i)

    >>>def call(i, func):

    func(i)>>> call((1,2,3,4,5),call_back)

(1, 2, 3, 4, 5)

类对象

类是面向对象常用的方法,在python中定义了类也是很“动态”的,前一篇文章说过,类相当于一个理想模型--模具,所以类实例都具有类的某些共同的特性,但每个类实例又有不同的地方,动态属性和动态方法也是造成这种差异的原因。再次强调,所谓动态,是指运行过程中的变化。

动态属性

>>>class Spider:

    def__init__(self, url):

        self.url=url# 要引用url属性,url就要作为参数添加到括号里>>> s=Spider(url='https://music.163.com/')# 实例化,不带括号则指向类本身而不是实例对象,由于__init__方法的原因,必须给类提供一个参数url>>> s.cookies="cookies"# 运行过程中绑定类属性>>> s.cookies'cookies'

动态方法

  要在运行过程中实现方法的绑定,需要借助于types模块中的MethodType方法,MethodType方法需要传递两个参数,第一个是function,第二个是instance。

>>>class Spider:

    def__init__(self, url):

        self.url=url    >>> s=Spider(url='https://music.163.com/')>>>import types>>>def parse(self):

    print('retrun the txt')

    >>> s.parse=types.MethodType(parse, s)>>> s.parse()

retrun the txt

面向对象(object)

  声明一下,以下全是个人在写这部分内容的想法细节,所以会很啰嗦@唐三藏,如果看官不想看,笔者不想浪费大家的时间。但笔者相信有人想看的,毕竟人最想看到的东西就是人自己遮住的东西。当然笔者更希望的是能帮助到别人,和笔者同样处境的人。

面向对象的基本思想

对象(object)是什么?可以理解为男朋友和女朋友吗?好像不太符合现阶段,好像也可以···这样理解比较复杂。笔者的理解是:对象是类的实例化。编程对象是具有特定标识地址、属性值和方法的事物,面向对象其实就是通过创建一个理想模型来“生产”对象,这个理想模型就是类,而对象就是一个个实例化后的类,对象具有类的属性和方法,方法往往可以调用属性。例如创建一个抽象模型--Person,赋予属性(name、age)和方法(say_hello),这个say_hello方法就是调用了P本身并利用了属性name的值。然后就可以这个类来“生产“不同的Person实例,比如Jack,比如mark···,

>>>class Person:

    def__init__(self, name, age):

        self.name=name

        self.age=age

    def say_hello(self):

        print('hello, my name is %s'%self.name)>>> P=Person('jack',23)            # 由于__init__方法的原因,必须提供两参数,不能这样实例化:P=Person()>>> P.name'jack'>>> P.age23>>> P.say_hello()# 这里就不用提供参数了,这也是方法(类的方法)和函数的唯一区别hello, my nameis jack>>> P=Person('mark',24)>>> P.name'mark'>>> P.age24>>> P.say_hello()

hello, my name is mark>>>

设计模式(工厂模式)

设计模式和面向对象密切相关,关于设计模式的内容太大,这里只简单说下工厂模式,工厂模式属于创建型模式,又可分为简单工厂模式、工厂方法模式、抽象工厂模式三种。这里以设计模式教程中的制造pizza为列子。再次重复强调,面向对象就是要创建理想模型,然后实例化这个模型继而模拟现实的物件。好吧,原谅我沉溺于柏拉图的理想模型吧(个人观点)。所以,我们要创建pizza的模型和实例化pizza对象!

        如何去设计pizza呢?依据pizza这个物件在现实情况,很容易就代入了pizza的卖家和买家,我们的理想模型要模拟的就是卖家,而实例化的过程是模拟买家的过程(最后我们的口号就是是卖家多卖钱,买家少花钱······,别信!)。

  如何去模拟卖家和买家呢?既然是面向对象,当先从对象——买家的角度出发,考虑到生活实际,当我们想吃pizza的时候,首先要看下有哪些可以种类的pizza选择,例如有海鲜pizza——SeafoodPizza,你想吃这个,然后你就会去买这个pizza或者网上订购这个piazza,假如是在网上订购,卖家肯定是需要提供给你进入这家piazza店——PizzaStore的一个店的入口,然后让顾客去选择这个SeafoodPizza?

  如何提供呢?我们要做的当然是写一个程序,当买家与这个程序交互的时候,提供piazza的种类给买家选择。所以,这个程序应该是什么样的呢?看官不用在意笔者的思路,不同的人有不同的观点,况且笔者的观点浅薄不堪!  笔者是这么想的,先定义一个SeafoodPiazza,然后再定义一个主程序作为入口,当顾客进入了这个入口,就可以选择自己喜欢的pizza,当顾客选择了自己想要的pizza之后,剩下的就是pizza店的事情了。

  那么pizza店在接收到订单之后要如何处理这个海鲜pizza订单呢?笔者不知道怎么做海鲜pizza,不过笔者知道做海鲜pizza的流程应该是一个自动化的流程,包含了一些步骤,而这些步骤将会是一些类方法,然后我们去调用这些方法去把pizza做出来。看到教程的做法是这样的,如此这般,准备pizza——prepare()、烤pizza——bake()、切pizza——cut()、 包装——box()。蛋刀直入,直接就创建一个海鲜pizza!

  定义好如何做pizza之后,又如何去调用这些方法呢?当然是"'对象'+'点'+'方法'”这样调用,而这个对象就是实例化的SeafoodPizza——SeafoodPizza()!代码如下(但请千万不要这么写):

class SeafoodPizza:

    def prepare(self):

        return'prepare seafood_pizza'def bake(self):

        return'bake seafood_pizza'def cut(self):

        return'cut seafood_pizza'def box(self):

        return'box seafood_pizza'deforderPizza(self):# 这个self就是我们下面将要实例化的SeafoodPizza——SeafoodPizza()print(self.prepare())

        print(self.bake())

        print(self.cut())

        print(self.box())def main():

    pizza = input('you will order seafood_pizza,please enter yes or no\n')# 放在循环外便于返回while True:

        ifpizza =='yes':

            pizza = SeafoodPizza()

            pizza.orderPizza()

            print('seafood_pizza completed')

            pizza = input('you have order seafood_pizza, anything else? yes or no\n ')

        elifpizza =='no':

            print('bye')

            returnelse:

            pizza = input('please enter yes or no\n')# 返回pizza获取用户输入if__name__=='__main__':

    main()

  好了,what,why,how,what,how,why;why,what,how;what······头晕······且慢,革命尚未成功,还不能倒下······(cry,hopeless······)

  如果单单是只经营一种pizza是可以这么写的,然鹅单单是一种显然是不够的,人都是喜新厌旧的嘛,面向对象也是这样,因为一个对象不可能满足顾客欲望的所有需求,所以要弄出多些花样,轮流去满足顾客才能维持这段供需关系。而且,作为生意人为了追求利益最大化肯定会拓展业务,增加产量,直到利润最大化,原理祥略。所以店长又增加了两种pizza模型——CheesePizza和ClamPizza。继续按照上面那样写也是可以的,加两段代码就可以了,可是此时的main()h函数就需要选择是哪种类型的pizza了。毕竟三个不算多,但如果数量增加到10个呢?代码太富态了!在获取输入方面问题其实是不大的。代码如下但千万别这么写!

 View Code

  那么如何写的简短点呢?很快,我们发现,虽然pizza不一样,但每种pizza的orderPizza的形式是一样的,所有有什么办法让它独立出来以达一劳永逸呢?因此我们要处理不同pizza的订单,那么我们应该创建一个专门处理订单的类,orderPizza将作为它的方法被调用,那么如何创建这个类以兼容不同的订单呢?无论这个类取啥名,只要它能够实现方法调用即可,但是如古装剧非常讲究名正言顺一样,起名适宜也是很重要的。例如:书上起的名字就是PizzaStore。

  既然PizzaStore要能够处理不同的订单,那么它的orderPizza方法就需要引入不同的类型判断,而类型可以通过特殊方法__int__初始化类型参数?代码如下,但请别这样搞。

 View Code


  不过见不得好到哪里去!如何进一步编呢?很快我们又发现了上述代码中orderPizza函数仍然是不变的,而类型判断那部分是随时有可能变化的,能不能让PizzaStore中变化的代码抽出去免得老是要添加来添加去呢?为了廉价付人工,把它交给制造工厂吧!这就是下面要是的简单工厂模式! 

工厂模式介绍

  在介绍简单工厂前,先看下工厂模式,书上是这么写的,在面向对象编程中,术语“工厂”表示一个负责创建其他类型对象的类。通常,这个类有一个对象以及多个和对象相关的多个方法,至于为什么要创建这样的一个类来创建对象呢?因为从上面的代码也可以看到,我们可以在main()函数里面实例化。那为什么还要这么做呢?当然因为这样做是由好吃的。

松耦合,即对象的创建可以独立于类的实现

客户端无需了解创建对象的类,也可以使用它来创建对象

轻松地在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端代码,客户端只需要传递一个参数即可。

工厂可以重用现有对象。但是客户端则总是创建一个新的对象。

  这些优点不要说没实践过,就算亲自实践过也可能不知道它说的啥,比如:按照上面的例子客户端也只需提供一个参数啊!不管了,还是继续往下写吧,人生苦短······

简单工厂模式

  很多介绍都说简单工厂不是一种模式,无论如何,笔者只想看看它的特征,嗯,笔者看到了,书上是这么写的:允许接口创建对象,但不会暴露对象的创建逻辑!参考前面的代码,如果要创一个“简单工厂”,就创一个SimPizzaFactory,再定义一个create_pizza函数,然后进行类型判断,然后在进行类型创建。在“工厂”里创建了类型对象,自然在PizzaStore类里面进可以不用再做这么多工作了,但是,要把工厂创建的对象导进来,如何导进来呢?对啦!寄望于魔术方法__init__!将“简单工厂”作为参数传递给PizzaStore来进行初始化,这样就可以将类型作为参数传递给orderPizza了。代码如下:      

 View Code

  好像也不见得什么啊,不过还是实现了将几乎不用改变的代码和经常需要变动的代码分离开,还是继续往下写吧,人生······

工厂方法模式

      一个“简单工厂”类实现了类型的实例化,无论有多少种pizza要做我都扔给工厂去做,问题到此似乎该告一段落了,然而人的欲望是不可能有尽头的,该来的还是来了,店长决定在全国都开起了分店。现在要怎么编呢?比如北京一个店,上海开一个······

  如果按照简单工厂前面的想法去写,应该也是可以的。但是通过不同方法的比较,我们已经认为简单工厂的方法目前是比较好的一种方式。所以这里就直接用简单工厂的方法去开分店了。

  现在制造pizza的类型要重新创建了,因为多了一个地域变量使得制造pizza的方法也各有不同,这些代码自然是不能省的,那么,工厂方法现在又要如何去处理(创建类型),自然很快就带来了一个问题:再不能仅仅依靠类似前面的类型判断去创建对象,因为不同分店的同种pizza是不同的!例如:北京分店的seafood_pizza和上海分店的seafood_pizza做法是有差别的!例如人们经常说的某酒必须要它当地的水才能酿成那种味道云云···,所以,在工厂还需确定是那种风格的pizza——北京风格还是上海风格?,再确定是那种口味的pizza,然后才能准确的调用相应的pizza种类。为此,我们可以通过创建不同种类的简单工厂来创建pizza类型。无论如何先帖代码,代码如下:

 View Code

  这个代码又笨又长,但笔者认为,只要每种pizza的制作方法不一样,有多少种pizza,就有多少段pizza代码,尽管代码很长,当然可以借助from ...import...来缩短代码,但总的代码数应当不能减少,当然也可以用继承,但是不够简单粗暴。其实创建简单工厂的时候也可以加入一个额外的参数变量——region,那样的判断和主函数的逻辑是一样的。另外,当pizza种类越来越多的时候,交互的内容也会越来越多,所以这里也可以定义一个help函数去简洁点处理。如此看来,简单工厂也是可以实现的,但是,也看到了这样的工厂在种类多起来的时候,工厂专业化程度越来越高,变动成本(重新调整逻辑的时间)越来越高!

  这时候怎么办呢?书上说要仰赖工厂方法,先看看工厂方法的特点:

我们定义了一个接口来创建,但是工厂本身并不负责创建对象,而是将这个任务交由子类来完成,即由子类决定了要实例化哪些类

工厂方法的创建是通过继承而不是通过实例化来完成的

工厂方法使设计更加具有可定制性。它可以返回相同的实例或子类,而不是某些类型的对象(就像在简单工厂方法中的那样)

  先看下代码:

 View Code

可以看到,相对来说,这里的代码貌似简洁了一点。这其中抛弃了简单工厂,从而将create_pizza函数搬到Pizza_Store工厂下面,并用抽象类的方法重新定义,然后让Pizza_Store工厂的子类BeijingPizzaStore和ShanghaiPizzaStore分店分别取实现自己的create_pizza,这也是工厂方法常用的方法。代码太长主要还是是piazza的种类的代码但必须要有,而交互随着pizza种类而也变得复杂。工厂方法的优点书上所说如下:

它具有更大的灵活性,使得代码更加通用,因为它不是单纯地实例化某个类。这样实现哪些类取决于接口(product),而不是ConcreteCreator类。

松耦合,因为创建对象的代码与使用它的代码是分开的。客户端无需关心传递哪些参数以及需要实例化哪些类。由于添加新类更加容易,所以降低了维护成本。

抽象工厂

  抽象工厂模式的主要目的是提供一个接口来创建一系列相关对象,而无需指定具体的类。工厂方法将创建实例的任务委托给了子类,而抽象工厂方法的目标是创建一系列相关对象。代码和书本差不多,所以大家看书即可,可能前面啰嗦太甚,这节草草了事凑下整,哈哈,看起来代码比上面的短,但仿照来写应该不会比上面的短很多,

你可能感兴趣的:(Python的语言特点)