首页 > 编程语言 > 详细

python基础教程_学习笔记9:抽象

时间:2019-04-20 14:09:46      阅读:133      评论:0      收藏:0      [点我收藏+]
版权声明:本文为博主原创文章。未经博主同意不得转载。 https://blog.csdn.net/signjing/article/details/30745465

抽象

懒惰即美德。

抽象和结构

抽象能够节省大量工作,实际上它的作用还要更大。它是使得计算机程序能够让人读懂的关键。

创建函数

函数能够调用(可能包含參数,也就是放在圆括号里的值),它运行某种行为而且返回一个值。一般来说,内建的callable函数能够用来推断函数是否可调用

>>>?import?math

>>>?y=1

>>>?x=math.sqrt

>>>?callable(x)

True

>>>?callable(y)

False

?

创建函数是组织程序的关键。

那么如何定义函数呢?

使用def(或“函数定义”)语句就可以:

>>>?def?hello(name):

return?‘Hello,?‘?+?name?+?‘!‘

?

传入不同的參数。得到不同的结果:

>>>?print?hello(‘signjing‘)

Hello,?signjing!

>>>?print?hello(‘jiao‘)

Hello,?jiao!

?

斐波那契数列的获取方法(比如,前10项)为:

>>>?f=[0,1]

>>>?for?i?in?range(8):

f.append(f[-1]+f[-2])

?

>>>?print?f

[0,?1,?1,?2,?3,?5,?8,?13,?21,?34]

?

假设用函数的方法实现。则为:

>>>?def?fibs(num):

result=[0,1]

for?i?in?range(num-2):

result.append(result[-2]+result[-1])

return?result

?

运行结果:

>>>?fibs(10)

[0,?1,?1,?2,?3,?5,?8,?13,?21,?34]

>>>?fibs(16)

[0,?1,?1,?2,?3,?5,?8,?13,?21,?34,?55,?89,?144,?233,?377,?610]

?

return语句是用来从函数中返回值的。

记录函数

假设想给函数写文档,让后面使用该函数的人能理解的话。能够增加凝视(以#开头)。

另外一个方式是直接写上字符串。

这里字符串在其它地方可能会非常实用,比方def语句后面(以及在模块或类的开头)。假设在函数的开头写下字符串,它就会成为函数的一部分进行存储。称为文档字符串

>>>?def?fibs(num):

‘fibs?is?a?funtion:*************‘

result=[0,1]

for?i?in?range(num-2):

result.append(result[-2]+result[-1])

return?result

?

>>>?fibs.__doc__

‘fibs?is?a?funtion:*************‘

?

注意:__doc__是函数属性。

内建的help函数非常实用。

在交互式解释器中使用它,就能够得到关于函数,包含它的文档字符串的信息。

>>>?help(fibs)

Help?on?function?fibs?in?module?__main__:

?

fibs(num)

????fibs?is?a?funtion:*************

?

并非真正函数的函数

数学意义上的函数,总在计算其參数后返回点什么。python的有些函数却并不返回不论什么东西。

没有return语句。或者虽有return语句但return后边没有跟不论什么值的函数不返回值。

?

>>>?def?test():

print?"This?is?printed"

return

print?‘this?is?not‘

?

>>>?x=test()

This?is?printed

上述函数中的return语句仅仅起到结束函数的作用。

>>>?x

>>>?print?x

None

?

所以,全部的函数的确都返回了东西:当不须要它们返回值的时候。它们就返回None

參数魔法

值从哪里来

写在def语句中函数名后面的变量通常叫函数的形式參数,而调用函数时提供的值是实际參数,或者成为參数。

我能改变參数吗?

在函数内为參数赋予新值不会改变外部不论什么变量的值。

>>>?def?try_to_change(n):

n="Hello?,?signjing"

?

>>>?say="Hello?,?jiao"

>>>?try_to_change(say)

>>>?say

‘Hello?,?jiao‘

?

字符串(以及数字和元组)是不可变的。即无法被改动。所以它们做參数的时候也就无需多做介绍。

但假设将可变的数据结构如列表做參数的时候会发生什么:

?

>>>?def?change(n):

n[0]=‘signjing‘

?

>>>?names=[‘Li?lei‘,‘Han?meimei‘]

>>>?change(names)

>>>?names

[‘signjing‘,?‘Han?meimei‘]

?

以下不用函数调用再做一次:

>>>?names=[‘Li?lei‘,‘Han?meimei‘]

>>>?n=names

>>>?n[0]=‘signjing‘

>>>?names

[‘signjing‘,?‘Han?meimei‘]

?

之前也出现过这样的情况:当两个变量同一时候引用一个列表的时候,它们的确是同一时候引用一个列表。

假设想避免这样的情况。能够复制一个列表的副本。当在序列中做切片的时候,返回的切片总是一个副本。因此。假设你复制了整个列表的切片,将会得到一个副本:

>>>?n=names[:]

>>>?n

[‘Li?lei‘,?‘Han?meimei‘]

>>>?names

[‘Li?lei‘,?‘Han?meimei‘]

>>>?n?is?names

False

>>>?m=n

>>>?m?is?n

True

?

?

在某些语言(如c++Ada)中,重绑定參数而且使这些改变影响到函数外的变量是非常寻常的事情。

但在python中是不可能的。函数仅仅能改动參数对象本身。

但假设參数不可变,如数字。又该怎么办呢?答案是没有办法。

这时候应该从函数中返回全部须要的值,假设值多于一个。则以元组形式返回。

比如。将变量数值增1的函数能够这样写:

>>>?def?inc(x):return?x+1

?

>>>?foo=10

>>>?foo=inc(foo)

>>>?foo

11

?

假设真的想改变參数,能够使用一点小技巧。即将值放置在列表中:

>>>?def?inc(x):x[0]=x[0]+1

?

>>>?foo=[10]

>>>?inc(foo)

>>>?foo

[11]

这样就仅仅会返回新值。

?

keyword參数和默认值

眼下我们所使用的參数都叫做位置參数,由于它们的位置非常重要——其实比它们的名字更重要。

>>>?def?hello_1(greeting,name):

print?‘%s,%s‘?%(greeting,name)

?

>>>?def?hello_2(name,greeting):

print?‘%s,%s‘?%(name,greeting)

?

>>>?hello_1(‘hello‘,‘boy‘)

hello,boy

>>>?hello_2(‘hello‘,‘girl‘)

hello,girl

?

有些时候(尤其是參数非常多的时候)。參数的顺序是非常难记住的。为了让事情简单些,能够提供參数的名字:

>>>?hello_1(greeting=‘hello‘,name=‘boy‘)

hello,boy

>>>?hello_1(name=‘boy‘,greeting=‘hello‘)

hello,boy

但參数名和值一定要相应:

>>>?hello_2(name=‘boy‘,greeting=‘hello‘)

boy,hello

>>>?hello_2(greeting=‘hello‘,name=‘boy‘)

boy,hello

?

这类使用參数名提供的參数叫做keyword參数

主要作用是明白每一个參数的作用。

keyword參数最厉害的地方在于能够在函数中给參数提供默认值。当參数具有默认值的时候。调用的时候就不用提供參数了。能够不提供、提供一些或者提供全部的參数:

>>>?def?hello_3(greeting=‘hello‘,name=‘world‘):

print?‘%s,%s!‘?%(greeting,name)

?

>>>?hello_3()

hello,world!

>>>?hello_3(‘Greeting‘)

Greeting,world!

>>>?hello_3(‘Greeting‘,‘universe‘)

Greeting,universe!

>>>?hello_3(name=‘boys‘)

hello,boys!

?

位置和keyword參数是能够联合使用的。

把位置參数放置在前面就能够了。

?

注意:除非全然清楚程序的功能和參数的意义。否则应该避免混合使用位置參数和keyword參数。

?

收集參数

有时候让用户提供随意数量的參数是非常实用的。

试着像以下这样定义函数:

>>>?def?print_params(*params):

print?params

?

>>>?print_params(1,2)

(1,?2)

>>>?print_params(1,2,‘ab‘)

(1,?2,?‘ab‘)

?

參数前的星号将全部值放置在同一个元组中。能够说是将这些值收集起来,然后使用。

>>>?def?print_params_2(title,*params):

print?title

print?params

?

>>>?print_params_2(‘Params:?‘,1,2,3)

Params:?

(1,?2,?3)

?

假设不提供不论什么供收集的元素,params就是空元组:

>>>?print_params_2(‘Nothing:?‘)

Nothing:?

()

?

>>>?print_params_2(‘Hmm...‘,something=42)

?

Traceback?(most?recent?call?last):

??File?"<pyshell#73>",?line?1,?in?<module>

????print_params_2(‘Hmm...‘,something=42)

TypeError:?print_params_2()?got?an?unexpected?keyword?argument?‘something‘

我们须要另外一个能处理keyword參数的“收集”操作。

>>>?def?print_params_3(**params):

print?params

?

>>>?print_params_3(x=1,y=2,z=3)

{‘y‘:?2,?‘x‘:?1,?‘z‘:?3}

?

?

反转过程

>>>?def?add(x,y):

return?x+y

?

>>>?params=(1,2)

>>>?add(*params)

3

在调用中使用而不是在定义中使用。

?

对于參数列表来说工作正常。仅仅要扩展到部分是最新的就能够。能够使用相同的技术来处理字典——使用双星号运算符。

?

>>>?def?hello_3(greeting=‘hello‘,name=‘world‘):

print?‘%s,?%s!‘?%(greeting,name)

?

>>>?params={‘name‘:‘Sir?Robin‘,‘greeting‘:‘Well?met‘}

>>>?hello_3(*params)

name,?greeting!

>>>?hello_3(**params)

Well?met,?Sir?Robin!

?

星号仅仅在定义函数(同意使用不定数目的參数)或者调用(“切割”字典或者序列)时才实用。

?

作用域

变量和所相应的值用的是个“不可见”的字典。

实际上这么说已经非常接近真实情况了。内建的vars函数能够返回这个字典:

>>>?x=1

>>>?scope=vars()

>>>?scope[‘x‘]

1

>>>?scope[‘x‘]+=1

>>>?x

2

?

这类“不可见字典”叫做命名空间或者作用域。究竟有多少个命名空间?除了全局作用域外,每一个函数调用都会创建一个新的作用域。

?

參数的工作原理相似于局部变量,所以用全局变量的名字作为參数名并没有问题。

?

假设须要在函数内部訪问全局变量,应该怎么办呢?而且仅仅想读取变量的值(也就是说不想重绑定变量),一般来说是没有问题的:

>>>?def?combine(parameter):

print?parameter+external

?

>>>?external=‘berry‘

>>>?combine(‘Shrub‘)

Shrubberry

?

读取全局变量一般来说并非问题,可是还是有个会出问题的事情。假设局部变量或者參数的名字和想要訪问的全局变量名相同的话,就不能直接訪问了。全局变量会被局部变量屏蔽。

假设的确须要的话,能够使用globals函数获取全局变量值。该函数的近亲是vars,它能够返回全局变量的字典(locals返回局部变量的字典)。

?

重绑定全局变量(使变量引用其它新值):假设在函数内部将值赋予一个变量。它会自己主动成为局部变量——除非告知python将其声明为全局变量。

>>>?x=1

>>>?def?change_global():

global?x

x=x+1

?

>>>?change_global()

>>>?x

2

递归

想到了一个笑话

你要想理解递归,首先得理解递归。

?

好吧。有点冷,继续热乎的话题....

?

递归的定义(包含递归函数定义)包含它们自身定义内容的引用。

须要查找递归的意思。结果它告诉请參见递归,无穷尽也,一个相似的函数定义例如以下:

>>>?def?recursion():

return?recursion()

?

显然它什么也做不了,理论上讲。它应该永远运行下去。

?

由于每次调用函数都会用掉一点内存,在足够的函数调用发生后,空间就不够了,程序以一个“超过最大递归深度”的错误信息结束:

Traceback?(most?recent?call?last):

??File?"<pyshell#37>",?line?1,?in?<module>

????recursion()

??File?"<pyshell#36>",?line?2,?in?recursion

return?recursion()

......

??File?"<pyshell#36>",?line?2,?in?recursion

????return?recursion()

RuntimeError:?maximum?recursion?depth?exceeded

?

这类递归叫无穷递归,相似于while?True開始的无穷循环。中间没有breakreturn语句。

?

实用的递归函数包含以下几个部分:

当函数直接返回值时有基本实例(最小可能性问题);

递归实例,包含一个或者多个问题最小部分的递归调用;

?

两个经典:阶乘和幂

>>>?def?factorial(n):

result=n

for?i?in?range(1,n):

result*=i

return?result

?

>>>?factorial(5)

120

?

递归的实现方式:

>>>?def?factorial(n):

if?n==1:

return?1

else:

return?n*factorial(n-1)

>>>?factorial(4)

24

?

>>>?def?power(x,n):

result=1

for?i?in?range(n):

result*=x

return?result

?

>>>?power(5,3)

125

递归实现:

>>>?def?power(x,n):

if?n==0:

return?1

else:

return?x*power(x,n-1)

?

>>>?power(4,4)

256

还有一个经典:二元查找

此处略;

对象的魔力

创建自己的对象(尤其是类型或者被称为类的对象)是python的核心概念——非常核心。

?

面向对象程序设计中的术语对象基本上能够看作数据(特性)以及由一系列能够存取、操作这些数据的方法所组成的集合。

对象最重要的长处包含以下几个方面:

多态:能够对不同类的对象使用相同的操作;

封装:对外部世界隐藏对象的工作细节。

继承:以普通的类为基础建立专门的类对象。

类和类型

类究竟是什么

类就是一种对象。全部的对象都属于某一个类,成为类的实例。

?

当一个对象所属的类是另外一个对象所属类的子集时。前者就被称为后者的子类。相反,后者就称为前者的超类(基类)

?

在面向对象程序设计中,子类的关系是隐式的,由于一个类的定义取决于它所支持的方法。定义子类仅仅是定义很多其它(也有可能是重载已经存在的)的方法的过程。

创建自己的类

>>>?__metaclass__?=?type

>>>?class?Person:

?

def?setName(self,name):

self.name=name

def?getName(self):

return?self.name

def?greet(self):

print?"Hello,world!I‘m?%s."?%self.name

?

>>>?foo=Person()

>>>?bar=Person()

>>>?foo.setName(‘abc‘)

>>>?foo.getName()

‘abc‘

>>>?foo.greet()

Hello,world!I‘m?abc.

?

self是对于对象自身的引用。

没有它,成员方法就没法訪问他们要对其特性进行操作的对象本身了。

?

特效、函数和方法

默认情况下。程序能够从外部訪问一个对象的特性。

为了让方法或特性变为私有(从外部无法訪问),仅仅要在它的名字前面加上双下划线就可以:

>>>?class?Secretive:

def?__inaccessible(self):

print?"Bet?you?can‘t?see?me..."

def?accessible(self):

print?"The?secret?message?is:"

self.__inaccessible()

?

>>>?s.__inaccessible()

?

Traceback?(most?recent?call?last):

??File?"<pyshell#52>",?line?1,?in?<module>

????s.__inaccessible()

AttributeError:?‘Secretive‘?object?has?no?attribute?‘__inaccessible‘

>>>?s.accessible()

The?secret?message?is:

Bet?you?can‘t?see?me...

?

类的内部定义中,全部以双下划线開始的名字都被“翻译”成前面加上单下划线和类名的形式:

>>>?Secretive._Secretive__inaccessible

<unbound?method?Secretive.__inaccessible>

?

简而言之,确保其它人不会訪问对象的方法和特性是不可能的,可是这类“名称变化术”就是他们不应该訪问这些函数或者特性的强有力信号。

类的命名空间

类的定义其实就是运行代码块,这一点非常实用。

指定超类

将其它类名写在class语句后的圆括号内能够指定超类:

>>>?class?Filter:

def?init(self):

self.blocked=[]

def?filter(self,sequence):

return?[x?for?x?in?sequence?if?x?not?in?self.blocked]

?

>>>?class?SPAMFilter(Filter):

def?init(self):

self.blocked=[‘SPAM‘]

?

?

Filter是个用于过滤序列的通用类。其实它不能过滤不论什么东西:

>>>?f=Filter()

>>>?f.init()

>>>?f.filter([1,3,4])

[1,?3,?4]

?

Filter类的用处在于它能够用作其它类的基类(超类),能够将序列中的“SPAM”过滤出去。

>>>?s=SPAMFilter()

>>>?s.init()

>>>?s.filter([‘abc‘,‘SPAM‘,"SPAM",‘SPAM‘,‘signjing‘])

[‘abc‘,?‘signjing‘]

调查继承

想要查看一个类是否是还有一个的子类。能够使用内建的issubclass函数:

>>>?issubclass(SPAMFilter,Filter)

True

>>>?issubclass(Filter,SPAMFilter)

False

?

假设想要知道已知类的基类(们),能够直接使用它的特殊特性__bases__

>>>?SPAMFilter.__bases__

(<class?‘__main__.Filter‘>,)

>>>?Filter.__bases__

(<type?‘object‘>,)

?

相同。还能使用isinstance方法检查一个对象是否是一个类的实例:

>>>?isinstance(s,SPAMFilter)

True

>>>?isinstance(s,Filter)

True

>>>?isinstance(s,str)

False

?

SSPAMFilter类的(直接)成员。但也是Filter类的间接成员,由于SPAMFilterFilter的子类。

?

假设想知道一个对象属于哪个类,能够使用__class__特性:

>>>?s.__class__

<class?‘__main__.SPAMFilter‘>

多个超类

>>>?class?Calculator:

def?calculate(self,expression):

self.value=eval(expression)

?

>>>?class?Talker:

def?talk(self):

print?‘Hi,my?value?is‘,self.value

?

>>>?class?TalkingCalculator(Calculator,Talker):

pass

?

超类能够有多个。

在这里,子类不做不论什么事,从自己的超类继承全部的行为。

这样的行为成为多重继承。是个非常实用的工具。

使用多重继承时,有个须要注意的地方。假设一个方法从多个超类继承,必须要注意一下超类的顺序:

先继承的类中的方法会重写后继承的类中的方法。

python基础教程_学习笔记9:抽象

原文:https://www.cnblogs.com/xfgnongmin/p/10740771.html

(0)
(0)
   
举报
评论 一句话评论(0
分享档案
最新文章
教程昨日排行
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!