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

百度公司做网站服务黑马程序员论坛

百度公司做网站服务,黑马程序员论坛,出售企业网站备案资料,如何在网站做404页面大家好#xff0c;欢迎来到Crossin的编程教室#xff01;众所周知#xff0c;我们可以通过索引值(或称下标)来查找序列类型(如字符串、列表、元组…)中的单个元素#xff0c;那么#xff0c;如果要获取一个索引区间的元素该怎么办呢#xff1f;切片(slice)就是一种截取索… 大家好欢迎来到Crossin的编程教室众所周知我们可以通过索引值(或称下标)来查找序列类型(如字符串、列表、元组…)中的单个元素那么如果要获取一个索引区间的元素该怎么办呢切片(slice)就是一种截取索引片段的技术借助切片技术我们可以十分灵活地处理序列类型的对象。通常来说切片的作用就是截取序列对象然而对于非序列对象我们是否有办法做到切片操作呢在使用切片的过程中有什么要点值得重视又有什么底层原理值得关注呢本文将主要跟大家一起来探讨这些内容希望我能与你共同学习进步。1、切片的基础用法列表是 Python 中极为基础且重要的一种数据结构也是最能发挥切片的用处的一种数据结构所以在前两节我将以列表为例介绍切片的一些常见用法。首先是切片的书写形式[i : in : m] 其中i 是切片的起始索引值为列表首位时可省略in 是切片的结束位置为列表末位时可省略m 可以不提供默认值是1不允许为0 当m为负数时列表翻转。注意这些值都可以大于列表长度不会报越界。切片的基本含义是从序列的第i位索引起向右取到后n位元素为止按m间隔过滤 。li  [1, 4, 5, 6, 7, 9, 11, 14, 16]# 以下写法都可以表示整个列表其中 X  len(li)li[0:X]  li[0:]  li[:X]  li[:]  li[::]  li[-X:X]  li[-X:]li[1:5]  [4,5,6,7] # 从1起取5-1位元素li[1:5:2]  [4,6] # 从1起取5-1位元素按2间隔过滤li[-1:]  [16] # 取倒数第一个元素li[-4:-2]  [9, 11] # 从倒数第四起取-2-(-4)2位元素li[:-2]  li[-len(li):-2]  [1,4,5,6,7,9,11] # 从头开始取-2-(-len(li))7位元素# 步长为负数时列表先翻转再截取li[::-1]  [16,14,11,9,7,6,5,4,1] # 翻转整个列表li[::-2]  [16,11,7,5,1] # 翻转整个列表再按2间隔过滤li[:-5:-1]  [16,14,11,9] # 翻转整个列表取-5-(-len(li))4位元素li[:-5:-3]  [16,9] # 翻转整个列表取-5-(-len(li))4位元素再按3间隔过滤# 切片的步长不可以为0li[::0]  # 报错(ValueError: slice step cannot be zero)上述的某些例子对于初学者(甚至很多老手)来说可能还不好理解但是它们都离不开切片的基本语法所以为方便起见我将它们也归入基础用法中。对于这些样例我个人总结出两条经验(1)牢牢记住公式[i : in : m]当出现缺省值时通过想象把公式补全(2)索引为负且步长为正时按倒数计算索引位置索引为负且步长为负时先翻转列表再按倒数计算索引位置。2、切片的高级用法一般而言切片操作的返回结果是一个新的独立的序列。以列表为例列表切片后得到的还是一个列表占用新的内存地址。当取出切片的结果时它是一个独立对象因此可以将其用于赋值操作也可以用于其它传递值的场景。但是切片只是浅拷贝 它拷贝的是原列表中元素的引用所以当存在变长对象的元素时新列表将受制于原列表。li  [1, 2, 3, 4]ls  li[::]li  ls # Trueid(li)  id(ls) # Falseli.append(li[2:4]) # [1, 2, 3, 4, [3, 4]]ls.extend(ls[2:4]) # [1, 2, 3, 4, 3, 4]# 下例等价于判断li长度是否大于8if(li[8:]):    print(not empty)else:    print(empty)# 切片列表受制于原列表lo  [1,[1,1],2,3]lp  lo[:2] # [1, [1, 1]]lo[1].append(1) # [1, [1, 1, 1], 2, 3]lp # [1, [1, 1, 1]]由于可见将切片结果取出它可以作为独立对象使用但是也要注意是否取出了变长对象的元素。切片既可以作为独立对象被“取出”原序列也可以留在原序列作为一种占位符使用。不久前我介绍了几种拼接字符串的方法其中三种格式化类的拼接方法(即 %、format()、template)就是使用了占位符的思想。对于列表来说使用切片作为占位符同样能够实现拼接列表的效果。特别需要注意的是给切片赋值的必须是可迭代对象。li  [1, 2, 3, 4]# 在头部拼接li[:0]  [0] # [0, 1, 2, 3, 4]# 在末尾拼接li[len(li):]  [5,7] # [0, 1, 2, 3, 4, 5, 7]# 在中部拼接li[6:6]  [6] # [0, 1, 2, 3, 4, 5, 6, 7]# 给切片赋值的必须是可迭代对象li[-1:-1]  6 # (报错TypeError: can only assign an iterable)li[:0]  (9,) #  [9, 0, 1, 2, 3, 4, 5, 6, 7]li[:0]  range(3) #  [0, 1, 2, 9, 0, 1, 2, 3, 4, 5, 6, 7]上述例子中若将切片作为独立对象取出那你会发现它们都是空列表即 li[:0]li[len(li):]li[6:6][] 我将这种占位符称为“纯占位符”对纯占位符赋值并不会破坏原有的元素只会在特定的索引位置中拼接进新的元素。删除纯占位符时也不会影响列表中的元素。与“纯占位符”相对应“非纯占位符”的切片是非空列表对它进行操作(赋值与删除)将会影响原始列表。如果说纯占位符可以实现列表的拼接那么非纯占位符可以实现列表的替换。li  [1, 2, 3, 4]# 不同位置的替换li[:3]  [7,8,9] # [7, 8, 9, 4]li[3:]  [5,6,7] # [7, 8, 9, 5, 6, 7]li[2:4]  [a,b] # [7, 8, a, b, 6, 7]# 非等长替换li[2:4]  [1,2,3,4] # [7, 8, 1, 2, 3, 4, 6, 7]li[2:6]  [a]  # [7, 8, a, 6, 7]# 删除元素del li[2:3] # [7, 8, 6, 7]切片占位符可以带步长从而实现连续跨越性的替换或删除效果。需要注意的是这种用法只支持等长替换。li  [1, 2, 3, 4, 5, 6]li[::2]  [a,b,c] # [a, 2, b, 4, c, 6]li[::2]  [0]*3 # [0, 2, 0, 4, 0, 6]li[::2]  [w] # 报错attempt to assign sequence of size 1 to extended slice of size 3del li[::2] # [2, 4, 6]3、自定义对象实现切片功能切片是 Python 中最迷人最强大最 Amazing 的语言特性(几乎没有之一)以上两小节虽然介绍了切片的基础用法与高级用法但这些还不足以充分地展露切片的魅力所以在接下来的两章节中我们将聚焦于它的更高级用法。前两节内容都是基于原生的序列类型(如字符串、列表、元组……)那么我们是否可以定义自己的序列类型并让它支持切片语法呢更进一步我们是否可以自定义其它对象(如字典)并让它支持切片呢3.1、魔术方法getitem()想要使自定义对象支持切片语法并不难只需要在定义类的时候给它实现魔术方法 __getitem__() 即可。所以这里就先介绍一下这个方法。语法object.__getitem__(self, key)官方文档释义Called to implement evaluation of self[key]. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence type) is up to the __getitem__() method. If key is of an inappropriate type, TypeError may be raised; if of a value outside the set of indexes for the sequence (after any special interpretation of negative values), IndexError should be raised. For mapping types, if key is missing (not in the container), KeyError should be raised.概括翻译一下__getitem__() 方法用于返回参数 key 所对应的值这个 key 可以是整型数值和切片对象并且支持负数索引如果 key 不是以上两种类型就会抛 TypeError如果索引越界会抛 IndexError 如果定义的是映射类型当 key 参数不是其对象的键值时则会抛 KeyError 。3.2、自定义序列实现切片功能接下来我们定义一个简单的 MyList 并给它加上切片功能。(PS仅作演示不保证其它功能的完备性)。import numbersclass MyList():    def __init__(self, anylist):        self.data  anylist    def __len__(self):        return len(self.data)    def __getitem__(self, index):        print(key is :   str(index))        cls  type(self)        if isinstance(index, slice):            print(data is :   str(self.data[index]))            return cls(self.data[index])        elif isinstance(index, numbers.Integral):            return self.data[index]        else:            msg  {cls.__name__} indices must be integers            raise TypeError(msg.format(clscls))l  MyList([My, name, is, Python猫])### 输出结果key is : 3Python猫key is : slice(None, 2, None)data is : [My, name]__main__.mylist0x0000019CD83A7A90key is : hiTraceback (most recent call last):...TypeError: MyList indices must be integers or slices从输出结果来看自定义的 MyList 既支持按索引查找也支持切片操作这正是我们的目的。3.3、自定义字典实现切片功能切片是序列类型的特性所以在上例中我们不需要写切片的具体实现逻辑。但是对于其它非序列类型的自定义对象就得自己实现切片逻辑。以自定义字典为例(PS仅作演示不保证其它功能的完备性)class MyDict():    def __init__(self):        self.data  {}    def __len__(self):        return len(self.data)    def append(self, item):        self.data[len(self)]  item    def __getitem__(self, key):        if isinstance(key, int):            return self.data[key]        if isinstance(key, slice):            slicedkeys  list(self.data.keys())[key]            return {k: self.data[k] for k in slicedkeys}        else:            raise TypeErrord  MyDict()d.append(My)d.append(name)d.append(is)d.append(Python猫)print(d[2])print(d[:2])print(d[-4:-2])print(d[hi])### 输出结果is{0: My, 1: name}{0: My, 1: name}Traceback (most recent call last):...TypeError上例的关键点在于将字典的键值取出并对键值的列表做切片处理其妙处在于不用担心索引越界和负数索引将字典切片转换成了字典键值的切片最终实现目的。4、迭代器实现切片功能好了介绍完一般的自定义对象如何实现切片功能这里将迎来另一类非同一般的对象。迭代器是 Python 中独特的一种高级对象它本身不具备切片功能然而若能将它用于切片这便仿佛是锦上添花能达到如虎添翼的效果。所以本节将隆重地介绍迭代器如何实现切片功能。4.1、迭代与迭代器首先有几个基本概念要澄清迭代、可迭代对象、迭代器。迭代 是一种遍历容器类型对象(例如字符串、列表、字典等等)的方式例如我们说迭代一个字符串“abc”指的就是从左往右依次地、逐个地取出它的全部字符的过程。(PS汉语中迭代一词有循环反复、层层递进的意思但 Python 中此词要理解成单向水平线性 的如果你不熟悉它我建议直接将其理解为遍历。)那么怎么写出迭代操作的指令呢最通用的书写语法就是 for 循环。# for循环实现迭代过程for char in abc:    print(char, end )# 输出结果a b cfor 循环可以实现迭代的过程但是并非所有对象都可以用于 for 循环例如上例中若将字符串“abc”换成任意整型数字则会报错int object is not iterable .这句报错中的单词“iterable”指的是“可迭代的”即 int 类型不是可迭代的。而字符串(string)类型是可迭代的同样地列表、元组、字典等类型都是可迭代的。那怎么判断一个对象是否可迭代呢为什么它们是可迭代的呢怎么让一个对象可迭代呢要使一个对象可迭代就要实现可迭代协议即需要实现__iter__() 魔术方法换言之只要实现了这个魔术方法的对象都是可迭代对象。那怎么判断一个对象是否实现了这个方法呢除了上述的 for 循环外我还知道四种方法# 方法1dir()查看__iter__dir(2)     # 没有略dir(abc) # 有略# 方法2isinstance()判断import collectionsisinstance(2, collections.Iterable)     # Falseisinstance(abc, collections.Iterable) # True# 方法3hasattr()判断hasattr(2,__iter__)     # Falsehasattr(abc,__iter__) # True# 方法4用iter()查看是否报错iter(2)     # 报错int object is not iterableiter(abc) # ### PS判断是否可迭代还可以查看是否实现__getitem__为方便描述本文从略。这几种方法中最值得一提的是 iter() 方法它是 Python 的内置方法其作用是将可迭代对象变成迭代器 。这句话可以解析出两层意思(1)可迭代对象跟迭代器是两种东西(2)可迭代对象能变成迭代器。实际上迭代器必然是可迭代对象但可迭代对象不一定是迭代器。两者有多大的区别呢如上图蓝圈所示普通可迭代对象与迭代器的最关键区别可概括为一同两不同 所谓“一同”即两者都是可迭代的(__iter__)所谓“两不同”即可迭代对象在转化为迭代器后它会丢失一些属性(__getitem__)同时也增加一些属性(__next__)。首先看看增加的属性 __next__ 它是迭代器之所以是迭代器的关键事实上我们正是把同时实现了 __iter__ 方法 和 __next__ 方法的对象定义为迭代器的。有了多出来的这个属性可迭代对象不需要借助外部的 for 循环语法就能实现自我的迭代/遍历过程。我发明了两个概念来描述这两种遍历过程(PS为了易理解这里称遍历实际也可称为迭代)它遍历 指的是通过外部语法而实现的遍历自遍历 指的是通过自身方法实现的遍历。借助这两个概念我们说可迭代对象就是能被“它遍历”的对象而迭代器是在此基础上还能做到“自遍历”的对象。ob1  abcob2  iter(abc)ob3  iter(abc)# ob1它遍历for i in ob1:    print(i, end   )   # a b cfor i in ob1:    print(i, end   )   # a b c# ob1自遍历ob1.__next__()  # 报错 str object has no attribute __next__# ob2它遍历for i in ob2:    print(i, end   )   # a b c    for i in ob2:    print(i, end   )   # 无输出# ob2自遍历ob2.__next__()  # 报错StopIteration# ob3自遍历ob3.__next__()  # aob3.__next__()  # bob3.__next__()  # cob3.__next__()  # 报错StopIteration通过上述例子可看出迭代器的优势在于支持自遍历同时它的特点是单向非循环的一旦完成遍历再次调用就会报错。对此我想到一个比方普通可迭代对象就像是子弹匣它遍历就是取出子弹在完成操作后又装回去所以可以反复遍历(即多次调用for循环返回相同结果)而迭代器就像是装载了子弹匣且不可拆卸的枪进行它遍历或者自遍历都是发射子弹这是消耗性的遍历是无法复用的(即遍历会有尽头)。写了这么多稍微小结一下迭代是一种遍历元素的方式按照实现方式划分有外部迭代与内部迭代两种支持外部迭代(它遍历)的对象就是可迭代对象而同时还支持内部迭代(自遍历)的对象就是迭代器按照消费方式划分可分为复用型迭代与一次性迭代普通可迭代对象是复用型的而迭代器是一次性的。 4.2、迭代器切片前面提到了“一同两不同”最后的不同是普通可迭代对象在转化成迭代器的过程中会丢失一些属性其中关键的属性是 __getitem__ 。在前一节中我已经介绍了这个魔术方法并用它实现了自定义对象的切片特性。那么问题来了为啥迭代器不继承这个属性呢首先迭代器使用的是消耗型的遍历这意味着它充满不确定性即其长度与索引键值对是动态衰减的所以很难 get 到它的 item 也就不再需要 __getitem__ 属性了。其次若强行给迭代器加上这个属性这并不合理正所谓强扭的瓜不甜……由此新的问题来了既然会丢失这么重要的属性(还包括其它未标识的属性)为什么还要使用迭代器呢这个问题的答案在于迭代器拥有不可替代的强大的有用的功能使得 Python 要如此设计它。限于篇幅此处不再展开后续我会专门填坑此话题。还没完死缠烂打的问题来了能否令迭代器拥有这个属性呢即令迭代器继续支持切片呢hi  欢迎关注公众号Python猫it  iter(hi)# 普通切片hi[-7:] # Python猫# 反例迭代器切片it[-7:] # 报错str_iterator object is not subscriptable迭代器因为缺少__getitem__ 因此不能使用普通的切片语法。想要实现切片无非两种思路一是自己造轮子写实现的逻辑二是找到封装好的轮子。Python 的 itertools 模块就是我们要找的轮子用它提供的方法可轻松实现迭代器切片。import itertools# 例1简易迭代器s  iter(123456789)for x in itertools.islice(s, 2, 6):    print(x, end   )   # 输出3 4 5 6for x in itertools.islice(s, 2, 6):    print(x, end   )   # 输出9# 例2斐波那契数列迭代器class Fib():    def __init__(self):        self.a, self.b  1, 1    def __iter__(self):        while True:            yield self.a            self.a, self.b  self.b, self.a  self.bf  iter(Fib())for x in itertools.islice(f, 2, 6):    print(x, end   )  # 输出2 3 5 8for x in itertools.islice(f, 2, 6):    print(x, end   )  # 输出34 55 89 144itertools 模块的 islice() 方法将迭代器与切片完美结合终于回答了前面的问题。然而迭代器切片跟普通切片相比前者有很多局限性。首先这个方法不是“纯函数”(纯函数需遵守“相同输入得到相同输出”的原则)其次它只支持正向切片且不支持负数索引这都是由迭代器的损耗性所决定的。那么我不禁要问itertools 模块的切片方法用了什么实现逻辑呢下方是官网提供的源码def islice(iterable, *args):    # islice(ABCDEFG, 2) -- A B    # islice(ABCDEFG, 2, 4) -- C D    # islice(ABCDEFG, 2, None) -- C D E F G    # islice(ABCDEFG, 0, None, 2) -- A C E G    s  slice(*args)    # 索引区间是[0,sys.maxsize]默认步长是1    start, stop, step  s.start or 0, s.stop or sys.maxsize, s.step or 1    it  iter(range(start, stop, step))    try:        nexti  next(it)    except StopIteration:        # Consume *iterable* up to the *start* position.        for i, element in zip(range(start), iterable):            pass        return    try:        for i, element in enumerate(iterable):            if i  nexti:                yield element                nexti  next(it)    except StopIteration:        # Consume to *stop*.        for i, element in zip(range(i  1, stop), iterable):            passislice() 方法的索引方向是受限的但它也提供了一种可能性即允许你对一个无穷的(在系统支持范围内)迭代器进行切片的能力。这是迭代器切片最具想象力的用途场景。除此之外迭代器切片还有一个很实在的应用场景读取文件对象中给定行数范围的数据。我们知道从文件中读取内容主要有两种方法(参见之前关于文件读写的文章)read() 适合读取内容较少的情况或者是需要一次性处理全部内容的情况而 readlines() 适用性更广因为它是迭代地读取内容既减少内存压力又方便逐行对数据处理。虽然 readlines() 有迭代读取的优势但它是从头到尾逐行读取若文件有几千行而我们只想要读取少数特定行(例如第1000-1009行)那它还是效率太低了。考虑到文件对象天然就是迭代器 我们可以使用迭代器切片先行截取然后再处理如此效率将大大地提升。# test.txt 文件内容猫Python猫python is a cat.this is the end.from itertools import islicewith open(test.txt,r,encodingutf-8) as f:    print(hasattr(f, __next__))  # 判断是否迭代器    content  islice(f, 2, 4)    for line in content:        print(line.strip())### 输出结果Truepython is a cat.this is the end.本节内容较多简单回顾一下迭代器是一种特殊的可迭代对象可用于它遍历与自遍历但遍历过程是损耗型的不具备循环复用性因此迭代器本身不支持切片操作通过借助 itertools 模块我们能实现迭代器切片将两者的优势相结合其主要用途在于截取大型迭代器(如无限数列、超大文件等等)的片段实现精准的处理从而大大地提升性能与效率。5、小结最后总结一下切片是 Python 的一种高级特性常用于截取序列类型的元素但并不局限于此本文主要介绍了它的基础用法、高级用法(如占位符用法)、自定义对象切片、以及迭代器切片等使用内容。除此之外切片还有更广阔多样的使用场景例如 Numpy 的多维切片、内存视图切片、异步迭代器切片等等都值得我们去探索一番今天限于篇幅先告一段落以后我们慢慢学习之。作者豌豆花下猫来源Python猫_往期文章推荐_浅谈链表-数据结构的重要根基
http://www.pierceye.com/news/414847/

相关文章:

  • 响应式网站设计的优点国内优秀设计网站
  • 网站开发集成环境国内html5网站欣赏
  • iis7.5 没有默认网站北京seo的排名优化
  • 两学一做网站是多少钱营销型网站策划怎么做
  • 渭南做网站的自建房设计图
  • 移动网站建设价格线上推广专员是干嘛的
  • 做化妆刷的外贸网站企业网站托管备案
  • 湖南省建设干部学校 网站金融直播室网站建设
  • 贵州建设厅特殊工种考试网站photoshop平面设计教学视频
  • 怎么推广我的网站代理网站推荐
  • wordpress主题站模板做网站跟做APP哪个容易
  • 杭州网站建设公司推荐网站建设优化服务渠道
  • php是网站开发语言吗做网站前端需要编程基础吗
  • python 网站开发 前端企业信用信息系统官网
  • 公司网站设计有哪些使用技巧呢商城网站建设怎么收费
  • 东莞做网站平台安阳营销型网站建设
  • 如何查看网站开发语言百度排行榜风云榜
  • 泉州 网站建设公司首选广告设计公司名字有寓意有创意
  • 天津个人做网站慈利网站制作
  • 专门做推广的网站吗宿迁房价2023年最新房价
  • 0基础12天精通网站建设网站建设 全网推广
  • 东莞网站营销推广公司移动应用开发案例
  • 妇科医院网站建设怎么做网站建设培训心得体会
  • 网站建设 管理正能量网站入口地址
  • 做网站没有创意Wordpress国际收款
  • 网站推广关键词工具wap网站分享到微信
  • 哪个网站可以给图片做链接做网站的公司在哪
  • 搬瓦工可以长期做网站广告制作开票大类是什么
  • 高级网站开发工信部小企业门户网站建设
  • 网站建站知识秦皇岛汽车网站制作