《流畅的Python》,优雅的python

  《流畅的Python》,优雅的python

  在Python中,函数是一级对象:

  创建一个元素,可以在运行时赋给变量或数据结构,可以作为参数传递给函数,可以是函数的返回结果。将函数视为object def factorial(n): #通过控制台会话在运行时创建函数。

  . 返回n!

  .如果n ^ 2否则返回1n *阶乘(n-1)

  .

  阶乘(42)

  1405006117752879898543142606244511569936384000000000

  阶乘。__doc__

  返回n!

  类型(阶乘)

  类“function”是function类的一个例子。让我们来看看作业,调用:

  事实=阶乘

  事实

  0x7f1c69958e18处的函数阶乘

  事实(5)

  120

  map(阶乘,range(11))

  0x7f1c62361908处的映射对象

  列表(映射(事实,范围(11)))

  【1,1,2,6,24,120,720,5040,40320,362880,3628800】对于一级函数,可以使用函数式风格编程。

  高阶函数接受函数作为参数,或者返回函数作为结果的函数就是高阶函数。map函数是,sorted()函数也是:可选的key参数可用于提供一个函数,该函数可应用于每个元素进行排序。

  根据单词长度对列表进行排序:

  水果=[草莓,无花果,苹果,樱桃,覆盆子,香蕉]

  已排序(水果,key=len)

  [FIG , Apple , Cherry , Banana , Raspberry , Strawberry]根据反向拼写对单词列表进行排序:

  定义反转(单词):

  .返回单词[:-1]

  .

  反向(“测试”)

  gnitset

  已分类(水果,键=反向)

  【香蕉,苹果,无花果,覆盆子,草莓,樱桃】香蕉的反向拼写以a开头,所以是第一个元素。

  函数式语言是map、filter和reduce的替代品,通常提供三个高阶函数:map、filter和reduce。

  求导和生成器表达式具有映射和过滤功能,更容易阅读。

  计算阶乘列表:映射和过滤器与列表派生之间的比较

  list(map(fact,range(6))) #0!敬5!的阶乘列表

  [1, 1, 2, 6, 24, 120]

  [范围(6)中n的fact(n)]#对列表派生做同样的事情。

  [1, 1, 2, 6, 24, 120]

  list(map(factorial,filter(lambda n: n % 2,range(6))))

  [1,6,120]#奇数阶乘列表(1!3!5!)

  [Factorial(n)for n in range(6)if n % 2]#使用列表派生而不是映射和筛选,同时避免lambda表达式。

  [1,6,120]reduce在Python3中的functools模块中,常用于求和,但最好使用内置的sum函数:

  从functools导入减少

  从操作员导入添加

  减少(增加,范围(100))

  4950

  总和(范围(100))

  950为了使用高阶函数,有时创建一次性的小函数更方便。这就是匿名函数存在的原因。

  匿名lambda关键字在Python表达式中创建匿名函数。

  Python将lambda函数的定义限制在纯表达式、赋值、while和try等。

  水果=[草莓,无花果,苹果,樱桃,覆盆子,香蕉]

  已排序(水果,key=lambda word: word[:-1])

  [香蕉,苹果,无花果,树莓,草莓,樱桃]可调用对象可以应用于除用户自定义函数之外的其他对象。调用运算符(即())也可以应用于其他对象。

  callable()函数可以用来确定一个对象是否可以被调用。

  abs,str,13

  (内置函数abs,class str ,13)

  [callable(obj) for obj in (abs,str,13)]

  [True,True,False]用户定义的可调用类型任何Python对象只要实现了实例方法__call__,就可以表现得像函数一样。

  例如,调用BingoCage实例从加扰列表中取出一个元素:

  # -*-编码:utf-8 -*

  随机导入

  BingoCage类:

  def __init__(self,items):

  自我。_items=list(items) #接受任何可迭代对象并在本地构建副本

  Random.shuffle(自我。_items) #心烦

  定义选择(自己):

  尝试:

  回归自我。_items.pop()

  exception error:#为空并引发异常。

  引发LookupError(“从空BingoCage中选取”)

  def __call__(self):

  return self.pick()

  if __name__==__main__ :

  bingo=BingoCage(range(3))

  print (bingo.pick()) #1

  Print(bingo()) # print2可以像函数一样调用

  Print(callable(bingo))#True这是将函数视为对象的另一个方面:运行时的自省。

  函数自省除了__doc__,函数对象还有很多属性。使用dir函数找出答案

  Factorial具有以下属性:

  阶乘

  [__annotations__ , __call__ , __class__ , __closure__ , __code__ ,

  __defaults__ , __delattr__ , __dict__ , __dir__ , __doc__ , __eq__ ,

  __format__ , __ge__ , __get__ , __getattribute__ , __globals__ ,

  __gt__ , __hash__ , __init__ , __kwdefaults__ , __le__ , __lt__ ,

  __module__ , __name__ , __ne__ , __new__ , __qualname__ , __reduce__ ,

  __reduce_ex__ , __repr__ , __setattr__ , __sizeof__ , __str__ ,

  __subclasshook__]这些属性大多数都是Python对象所共有的。本节讨论与将函数视为对象相关的问题。

  的几个属性,从__dict__开始。

  像用户定义的常规类一样,函数使用__dict__属性来存储分配给它的函数。

  家居属性。这相当于一种基本形式的注释。

  下面重点介绍函数独有的属性,这些属性不适用于一般用户定义的对象。计算两个属性集的差集,以获得特定于函数的属性列表:

  C类:通过

  obj=C()

  def func():通过

  sorted(set(dir(func))-set(dir(obj)))

  [_ _ annotations _ _ , _ _ call _ _ , _ _ closure _ _ , _ _ code _ _ , _ _ defaults _ _ , _ _ get _ _ , _ _ globals _ _

  类型

  解释

  __注释_ _

  词典

  以及对参数和返回值的注释。

  __呼叫_ _

  方法包装

  实现()运算符;可调用对象协议

  __关闭_ _

  元组

  函数闭包,即自由变量的绑定。

  __代码_ _

  密码

  编译成字节码的函数元数据和函数定义体

  __默认值_ _

  元组

  参数的默认值。

  __get__

  方法包装

  实现只读描述符协议

  __全局_ _

  词典

  函数所在模块中的全局变量

  __kwdefaults__

  词典

  仅关键字表单参数的默认值。

  __姓名_ _

  潜艇用热中子反应堆(submarine thermal reactor的缩写)

  函数名

  __qualname__

  潜艇用热中子反应堆(submarine thermal reactor的缩写)

  函数的限定名,如Random.choice

  从定位参数到只有关键字的参数,Python最好的特性之一是它提供了非常灵活的参数处理机制,而且Python

  3进一步提供了一个只有关键字的参数。与之密切相关的

  重要的是,在调用函数时,使用*和* *来“扩展”iterable对象,并将其映射到单个参数。

  数数。

  def标签(名称,*内容,cls=无,* *属性):

  “”生成一个或多个HTML标记

  :param name:定位参数(位置参数)

  :param content:变量参数(允许传入任何(或0)个参数,并通过tuple存储)

  :param cls: named keyword参数,需要一个*(或变量参数)作为分隔符,后跟named keyword参数,

  必须传入参数名。

  :param attrs:关键字参数,允许任何(或0个)带参数名的参数传入并由dict存储。

  :返回:

  如果cls不为None:

  attrs[class]=cls

  如果属性:

  attr_str= 。join( %s=%s % (attr,value) for attr,value in sorted(attrs.items()))

  否则:

  attr_str=

  如果内容:

  返回“\n”。join( %s%s %s /%s %(name,attr_str,c,name) for c in content)

  否则:

  Return %s%s/%(name,attr_str)让我们解释一下这些参数:

  名称:位置参数(positional parameter)*内容:变量参数(允许传入任意(或0)个参数,通过tuple存储)cls: only (named)关键字参数。命名关键字参数需要一个*(或变量参数)作为分隔符,这个分隔符后面的类似位置参数就是命名关键字参数。请注意,调用时必须传入参数名。Attrs:关键字参数,允许传入任意(或0)个带参数名的参数,并由dict存储。以下是调用的示例:

  Tag(br) #将单个参数传递给定位参数名称,并生成一个具有指定名称的空标记

  br /

  Tag(p , hello) #第一个参数之后的任何参数都会被*content捕获并存储在tuple中。

  p你好/p

  Tag(p , hello , world) #第一个参数之后的任何参数都会被*content捕获并存储在tuple中。

  p你好/p \n p世界/p

  标签( p , hello ,id=33) #标签函数签名中没有指定名称的关键字将被**attrs捕获并存储在字典中(此处为id)

  p id=33 你好/p

  Tag (P , Hello , World ,CLS= Sidebar) # CLS参数只能作为关键字参数传入,且必须传入参数名,否则无法与position参数区分。

  p你好/p \n p世界/p

  标签(内容=测试,名称=img )

  img内容=测试/

  my_tag={name: img , title :日落大道, src: sunset.jpg , cls: framed}

  Print(tag(**my_tag)) #my_tag前面是* *。字典中的所有元素都作为单个参数传入,同名的键绑定到相应的命名参数,其余的由**attrs捕获。

  img= sunset . jpg title= sunset boulevard /定义函数时,如果要指定仅关键字的参数,应该将它们放在前面带*的参数之后。如果你不想支持无限数量的定位参数,但只想支持关键字参数,在签名

  按如下方式填写a *:

  def f(a,*,b):

  .返回a,b

  .

  f(1,b=2)

  (1, 2)

  F(1,2) #参数名B必须公式化

  回溯(最近一次呼叫):

  模块中文件 stdin 的第1行

  类型错误:f()接受1个位置参数,但接受2个有关参数的信息。HTTP微框架Bobo中有一个很好的使用函数自省的例子。例子就是波波。

  教程中“Hello world”应用程序的改编说明了如何使用内省。

  进口bobo

  @bobo.query(/)

  def hello(人):

  返回“你好%s!”%Bobo检查hello函数,发现它需要一个名为

  参数,然后从请求中获取与该名称对应的参数,并将其传递给

  Hello函数,所以程序员根本不用接触被请求的对象。

  使用以下命令启动程序:

  波波-f波波_演示. py

  在8080端口上提供[bobo__main__].

  通过Postman访问http://localhost:8080,可以看到状态码是403,并且有一个person变量丢失的消息。

  让我们亲自尝试一下:

  Bobo怎么知道一个函数需要哪个参数?它如何知道参数没有默认值?

  该函数有一个__defaults__属性,它的值是一个元组,它包含

  定位参数和关键字参数的默认值。关键字参数的默认值仅在中

  __kwdefaults__属性。但是,参数的名称在__code__属性中,

  它的值是一个代码对象引用,它有许多属性。

  定义剪辑(text,max_len=80):

  在max_len之前或之后的第一个空格处截断文本

  end=无

  if len(text) max_len:

  space_before=text.rfind(,0,max_len)

  如果space_before=0:

  end=space_before

  否则:

  space_after=text.rfind(,max_len)

  如果space_after=0:

  end=空格_后

  如果end为None:

  end=len(文本)

  返回文本[:end]。rstrip()

  打印(剪辑。__defaults__) #(80,)

  打印(剪辑。_ _ code _ _)#0x 000001 f 1807759 c 0处的代码对象剪辑,文件“D:/workspace/python/fluent python/first _ class _ function/clip . py”,第3行

  打印(剪辑。__代码_ _。co_varnames) #(text , max_len , end , space_before , space_after )

  打印(剪辑。_ _代码_ _。co _ argcount) # 2可见这种组织信息的方式并不是最方便的。参数名称在

  __代码_ _。co_varnames,但是还有在函数定义体中创建的office。

  部分变量。因此,参数名是前n个字符串,n的值由下式确定

  __代码_ _。co_argcount当然。

  幸运的是,我们有一个更好的方法来使用——中的inspect模块。

  sig=签名(剪辑)

  print(sig) #(text,max_len=80)

  对于名称,sig.parameters.items()中的参数:

  print(param.kind,:,name,=,param.default)

  # POSITIONAL _ OR _ KEYWORD:text=class inspect。_empty

  # positive _ or _ keyword:max _ len=80 inspect。签名对象有一个绑定方法,可以绑定任何参数。

  用于确定签名中参数的规则与用于匹配参数的规则相同。结构

  您可以使用此方法在实际调用函数之前验证参数,如示例所示。

  进口检验

  Sig=inspect.signature(tag) #tag函数已经在上面定义了。

  my_tag={name: img , title :日落大道, src: sunset.jpg , cls: framed}

  bound_args=sig.bind(**my_tag)

  Bound_args # Get BoundArguments对象

  Boulevard参数(name=img ,cls=framed ,attrs={ title : Sunset Boulevard , src: sunset.jpg})

  对于名称,bound_args.arguments.items()中的值:# iterative bound _ args.arguments

  .打印(名称,=,值)

  .

  name=img

  cls=框架

  attrs={title :日落大道, src: sunset.jpg}

  Del _ tag [name] #删除必须从my_tag中指定的参数名

  Bound_args=sig.bind(**my_tag) #调用错误

  回溯(最近一次呼叫):

  模块中文件 stdin 的第1行

  文件“/usr/lib/python 3.6/lib/python 3.6/inspect . py”,第2933行,在bind中

  返回参数[0]。_bind(args[1:],kwargs)

  文件“/usr/lib/python 3.6/lib/python 3.6/inspect . py”,第2848行,in _bind

  从None引发TypeError(msg)

  类型错误:缺少必需的参数:“name”框架和IDE可以使用这些信息来验证代码。Python 3的另一个特性

  3354函数注释3354增强了该信息的使用。

  注Python 3提供了在函数声明中将元数据附加到参数和返回值的语法。

  以下是带注释的剪辑函数:

  DEF CLIP (text: str,max _ len: int0=80)-str: #带注释的函数声明

  在max_len之前或之后的第一个空格处截断文本

  end=无

  if len(text) max_len:

  space_before=text.rfind(,0,max_len)

  如果space_before=0:

  end=space_before

  否则:

  space_after=text.rfind(,max_len)

  如果space_after=0:

  end=空格_后

  如果end为None:

  end=len(文本)

  返回文本[:end]。rstrip()批注不会被处理,但会存储在函数的__annotations__属性(一个字典)中:

  打印(剪辑。__批注_ _)

  {text: class str , max _ len: int0 , return :class str } Python对注释做的唯一事情就是将它们存储在函数的

  __annotations__属性。仅此而已。Python不检查,不强制,

  没有验证什么都不做。

  将来,像Bobo这样的框架可以自动支持注释和进一步处理请求。举个例子,

  用price:float注释的参数可以自动将查询字符串转换成函数期望。

  的浮点类型;Quantity:可以转换类似“int0”的字符串注释。

  成对参数的验证。

  支持函数式编程的包运算符模块,在函数式编程中经常需要使用算术运算符作为函数。例如,不要使用

  递归阶乘计算。可以使用Sum函数,但是quadrature没有这样的函数。

  数数。我们可以使用reduce函数,但是我们需要一个函数来计算序列中的2。

  元素的乘积。这个例子展示了如何使用lambda表达式来解决这个问题。

  从functools导入减少

  定义事实(n):

  .return reduce (lambda a,b: a * b,range (1,n-1))运算符模块为多个算术运算符提供了相应的函数,从而避免了编写。

  Lambda a,b: a*b,一个普通的匿名函数。

  从functools导入减少

  从操作员导入mul

  定义事实(n):

  Return Reduce (Mul,Range (1,n-1))运算符模块中也有一类函数,可以代替从序列中取出元素或读取对象。

  属性的lambda表达式:因此,itemgetter和attrgetter实际上

  自我建构功能

  metro_data=[

  .(东京, JP ,36.933,(35.689722,139.691667)),

  .(“德里NCR”,“在”,21.935,(28.613889,77.208889)),

  .(墨西哥城, MX ,20.142,(19.433333,-99.133333)),

  .(纽约-纽瓦克,美国,20.104,(40.808611,-74.020386)),

  .(圣保罗, BR ,19.649,-23.547778,-46.635833),

  .]

  从运算符导入itemgetter

  对于已排序的城市(metro_data,key=itemgetter(1)): #

  .打印(城市)

  .

  (圣保罗, BR ,19.649,(-23.547778,-46.635833))

  (“德里NCR”,“在”,21.935,(28.613889,77.208889))

  (东京, JP ,36.933,(35.689722,139.691667))

  (墨西哥城, MX ,20.142,(19.433333,-99.133333))

  ( New York-Newark , US ,20.104,(40.808611,-74.020386))其实itemgetter(1)的函数和lambda fields: fields[1]:创建一个接受集合并返回索引位1的元素的函数。

  如果将多个参数传递给itemgetter,它构建的函数将返回提取的值结构。

  分成元组:

  cc_name=itemgetter(1,0)

  对于metro_data中的城市:

  .打印(抄送姓名(城市))

  .

  (“JP”,“东京”)

  (“在”,“德里NCR”)

  (“MX”,“墨西哥城”)

  (“美国”、“纽约-纽瓦克”)

  ( BR ,圣保罗)attrgetter和itemgetter的相似之处在于,它创建的函数是根据其名称提取的。

  对象的属性。如果将多个属性名传递给attrgetter,它也会返回fetch。

  一组值。此外,如果参数名包含。(点),attrgetter

  将深度嵌套对象并获得指定的属性。

  从集合导入命名元组

  LatLong=namedtuple (latlong , latlong) #使用namedtuple定义LatLong

  Metropolis=named tuple( Metropolis , name cc pop coord )

  metro_areas=[Metropolis(name,cc,pop,LatLong(lat,Long))

  .对于metro_data中的名称、抄送、弹出、(纬度、经度)

  metro_areas[0]

  Metropolis(name=Tokyo ,cc=JP ,pop=36.933,coord=LatLong(lat=35.689722,long=139.691667))

  Metro_areas[0].coord.lat#以获取其纬度

  35.689722

  从操作员导入attrgetter

  Name _ lat=attrgetter (Name , coord.lat) #获取name属性和嵌套的coord.lat属性

  对于已排序的城市(metro _ areas,key=attrgetter( coord . lat ):#使用attr getter并按纬度排序。

  .打印(姓名(城市))

  .

  (“圣保罗”,-23.547778)

  (“墨西哥城”,19.433333)

  (“德里NCR”,28.613889)

  (“东京”,35.689722)

  (‘纽约-纽瓦克’,40.808611)使用Metropolis实例建立metro_areas列表;注意,我们使用

  解包并提取嵌套元组(lat,long),然后用它们构建LatLong,

  作为大都会的协调财产。

  在操作员模块的剩余功能中,介绍最后一个。

  它的功能类似于attrgetter和itemgetter,它

  会创建自己的函数。methodcaller创建的函数将调用对象上的参数。

  设置方法:

  从运算符导入方法调用方

  “时候到了”

  Upcase=methodcaller(upper) #要调用str.upper方法

  大写字母

  时候到了

  hiphenate=method caller( replace , ,-)

  hiphenate

  “时间已到来”使用functools.partial冻结参数functools.partial。此高阶函数用于部分应用函数。部分响应

  使用是指基于一个函数创建一个新的可调用对象,并改变原函数的一些参数。

  修好了。使用此函数修改接受一个或多个参数的函数以进行回调。

  API,这样参数就少了。

  从操作员导入mul

  从functools导入部分

  Triple=partial(mul,3) # Fix参数3,所以只需要传递一个参数。

  三重(7)

  21

  列表(map(triple,range(1,10)))

  [3,6,9,12,15,18,21,24,27]用上一篇文章介绍的unicode.normalize函数再举一个例子。这个例子

  比较实用。如果您正在处理用多种语言编写的文本,在比较或排序之前,您可以

  希望使用unicode.normalize(NFC ,s)来处理所有字符串。例如

  如果您经常这样做,您可以定义一个nfc函数:

  导入unicodedata,functools

  nfc=func tools . partial(unicode data . normalize, NFC )

  s1=“咖啡馆”

  s2=cafe\u0301

  s1、s2

  (咖啡馆,咖啡馆)

  s1==s2

  错误的

  nfc(s1)==nfc(s2)

  True partial的第一个参数是一个可调用对象,后面是任何要绑定的对象。

  定位参数和关键字参数。这个功能其实很有用。

  原创作品来自愤怒的可乐,的博主,转载请联系作者取得转载授权,否则将追究法律责任。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: