Python中序列的修改、散列与切片详解

前言

本文主要给大家介绍了关于Python中序列的修改、散列与切片的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

Vector类:用户定义的序列类型

  我们将使用组合模式实现 Vector 类,而不使用继承。向量的分量存储在浮点数数组中,而且还将实现不可变扁平序列所需的方法。

Vector 类的第 1 版要尽量与前一章定义的 Vector2d 类兼容。

Vector类第1版:与Vector2d类兼容

Vector 类的第 1 版要尽量与前一章定义的 Vector2d 类兼容。然而我们会故意不让 Vector 的构造方法与 Vector2d 的构造方法兼容。为了编写 Vector(3, 4) 和 Vector(3, 4, 5) 这样的代码,我们可以让 __init__ 方法接受任意个参数(通过 *args);但是,序列类型的构造方法最好接受可迭代的对象为参数,因为所有内置的序列类型都是这样做的。

测试 Vector.__init__ 和 Vector.__repr__ 方法

>>> Vector([3.1, 4.2])
Vector([3.1, 4.2])
>>> Vector((3, 4, 5))
Vector([3.0, 4.0, 5.0])
>>> Vector(range(10))
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])

vector_v1.py:从 vector2d_v1.py 衍生而来

from array import array
import reprlib
import math

class Vector:
 typecode = 'd'

 def __init__(self, components):
 self._components = array(self.typecode, components)  #self._components是“受保护的”实例属性,把Vector的分量保存在一个数组中

 def __iter__(self):
 return iter(self._components)    #为了迭代,我们使用self._components构建一个迭代器

 def __repr__(self):
 components = reprlib.repr(self._components)   #使用reprlib.repr()函数获取self._components 的有限长度表示形式(如 array('d', [0.0, 1.0, 2.0, 3.0, 4.0, ...]))
 components = components[components.find('['):-1]  #把字符串插入 Vector 的构造方法调用之前,去掉前面的array('d' 和后面的 )
 return 'Vecotr({})'.format(components)   #直接使用 self._components 构建 bytes 对象

 def __str__(self):
 return str(tuple(self))

 def __bytes__(self):
 return (bytes([ord(self.typecode)]) +
  bytes(self._components))

 def __eq__(self, other):
 return tuple(self) == tuple(other)

 def __abs__(self):
 return math.hypot(sum(x * x for x in self))   #不能使用hypot方法了,因此我们先计算各分量的平方之和,然后再使用sqrt方法开平方

 def __bool__(self):
 return bool(abs(self))

 @classmethod
 def frombytes(cls, octets):
 typedcode = chr(octets[0])
 memv = memoryview(octets[1:]).cast(typedcode)
 return cls(memv)      #我们只需在 Vector2d.frombytes 方法的基础上改动最后一行:直接把memoryview传给构造方法,不用像前面那样使用*拆包

协议和鸭子类型

在 Python 中创建功能完善的序列类型无需使用继承,只需实现符合序列协议的方法。不过,这里说的协议是什么呢?

在面向对象编程中,协议是非正式的接口,只在文档中定义,在代码中不定义。例如,Python 的序列协议只需要 __len__ 和 __getitem__ 两个方法。任何类(如 Spam),只要使用标准的签名和语义实现了这两个方法,就能用在任何期待序列的地方。Spam 是不是哪个类的子类无关紧要,只要提供了所需的方法即可。

class FrenchDeck:
 ranks = [str(n) for n in range(2, 11)] + list('JQKA')
 suits = 'spades diamonds clubs hearts'.split()

 def __init__(self):
 self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]

 def __len__(self):
 return len(self._cards)

 def __getitem__(self, position):
 return self._cards[position]

协议是非正式的,没有强制力,因此如果你知道类的具体使用场景,通常只需要实现一个协议的部分。例如,为了支持迭代,只需实现__getitem__ 方法,没必要提供 __len__ 方法。

Vector类第2版:可切片的序列

如 FrenchDeck 类所示,如果能委托给对象中的序列属性(如self._components 数组),支持序列协议特别简单。下述只有一行代码的 __len__ 和 __getitem__ 方法是个好的开始:

class Vector:
 # 省略了很多行
 # ...

 def __len__(self):
 return len(self._components)

 def __getitem__(self, index):
 return self._components[index]

添加这两个方法之后,就能执行下述操作了:

>>> v1 = Vector([3, 4, 5])
>>> len(v1)
>>> v1[0], v1[-1]
(3.0, 5.0)
>>> v7 = Vector(range(7))
>>> v7[1:4]
array('d', [1.0, 2.0, 3.0])

可以看到,现在连切片都支持了,不过尚不完美。如果 Vector 实例的切片也是 Vector 实例,而不是数组,那就更好了。前面那个FrenchDeck 类也有类似的问题:切片得到的是列表。对 Vector 来说,如果切片生成普通的数组,将会缺失大量功能。

为了把 Vector 实例的切片也变成 Vector 实例,我们不能简单地委托给数组切片。我们要分析传给 __getitem__ 方法的参数,做适当的处理。

切片原理

了解 __getitem__ 和切片的行为

>>> class MySeq:
... def __getitem__(self, index):
...  return index
...
>>> s = MySeq()              
>>> s[1]                      #__getitem__直接返回传给它的值
>>> s[1:4]                     #[1:4]表示变成了slice(1, 4, None)
slice(1, 4, None)
>>> s[1:4:2]                    #[1:4:2]的意思为从第1个索引开始,到第4个索引结束,步长为2
slice(1, 4, 2)          
>>> s[1:4:2, 9]                  
(slice(1, 4, 2), 9)                #神奇的事情发生了..wtf...如果[]中有逗号,那么__getitem__接收的是元祖
>>> s[1:4:2, 7:9]                 #元祖中还可以包含多个切片对象
(slice(1, 4, 2), slice(7, 9, None))

🌰 查看slice类的属性

>>> slice
<class 'slice'>
>>> dir(slice)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', 

'__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', 

'__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 

'__sizeof__', '__str__', '__subclasshook__', 'indices', 'start', 'step', 'stop']

调用 dir(slice) 得到的结果中有个 indices 属性,这个方法有很大的作用,但是鲜为人知。help(slice.indices) 给出的信息如下。

indices(...)

S.indices(len) -> (start, stop, stride)

给定长度为 len 的序列,计算 S 表示的扩展切片的起始(start)和结尾(stop)索引,以及步幅(stride)。超出边界的索引会被截掉,这与常规切片的处理方式一样。

举个 🌰 假设有个长度为5的序列,例如'ABCDE':

>>> slice(None, 10, 2).indices(5)    #'ABCDE'[:10:2]等同于[:]
(0, 5, 2)
>>> slice(-3, None, None).indices(5)  #'ABCDE'[-3:]等同于[2:5:1]
(2, 5, 1)

能处理切片的__getitem__方法

vector_v2.py 的部分代码:为 vector_v1.py 中的 Vector类添加 __len__ 和__getitem__ 方法

def __len__(self):
 return len(self._components)

 def __getitem__(self, index):
 cls = type(self)      #获取实例的类(Vector)
 if isinstance(index, slice):    #判断传递进来的index是否为slice对象
  return cls(self._components[index])   #调用类的构造方法,创建一个新的Vector实例
 elif isinstance(index, numbers.Integral):   #如果传递进来的是个整数
  return self._components[index]    #返回 _components 中相应的元素
 else:       #抛出异常
  msg = '{cls.__name__} indices must be integers'
  raise TypeError(msg.format(cls=cls))

测试 🌰 改进的 Vector.__getitem__ 方法

>>> v7 = Vector(range(7))
>>> v7[-1]                     #获取单个整数索引,返回一个浮点数
6.0
>>> v7[1:4]                     #切片索引创建一个新的Vector实例
Vector([1.0, 2.0, 3.0])
>>> v7[-1:]                     #长度为1的切片也会创建一个新的Vector实例
Vector([6.0])
>>> v7[1,2]                     #我们在上面的slice已经知道,如果slice中包含了,则是一个包含slice的元祖,所以报错了
Traceback (most recent call last):
...
TypeError: Vector indices must be integers

Vector类第3版:动态存取属性

  Vector2d 变成 Vector 之后,就没办法通过名称访问向量的分量了(如 v.x 和 v.y)。现在我们处理的向量可能有大量分量。不过,若能通过单个字母访问前几个分量的话会比较方便。比如,用 x、y 和 z 代替 v[0]、v[1] 和 v[2]

  在 Vector2d 中,我们使用 @property 装饰器把 x 和 y 标记为只读特性。我们可以在 Vector 中编写四个特性,但这样太麻烦。特殊方法 __getattr__ 提供了更好的方式。

  属性查找失败后,解释器会调用 __getattr__ 方法。简单来说,对my_obj.x 表达式,Python 会检查 my_obj 实例有没有名为 x 的属性;如果没有,到类(my_obj.__class__ )中查找;如果还没有,顺着继承树继续查找。 如果依旧找不到,调用 my_obj 所属类中定义的__getattr__ 方法,传入 self 和属性名称的字符串形式(如 'x')。

vector_v3.py 的部分代码:在 vector_v2.py 中定义的Vector 类里添加 __getattr__ 方法

def __getattr__(self, name):
  cls = type(self)           #获取Vector

  if len(name) == 1:           #如果属性名只有一个字母,可能是shortcut_names中的一个
   pos = cls.shorcut_name.find(name)      #查找属性name是否在xyzx中的位置,如果木有就返回-1,有就返回对应的索引
   if 0 <= pos < len(self._components):     #判断这个查找到的索引是否在区间内
    return self._components[pos]      #返回查找的到的数组中的值
  msg = '{.__name__!r} object has no attribute {!r}'   #报错的信息
  raise AttributeError(msg.format(cls, name))     #抛出异常

测试下效果:

>>> v = Vector(range(5))
>>> v
Vector([0.0, 1.0, 2.0, 3.0, 4.0])
>>> v.x                # 使用v.x获取第一个元素v[0]
0.0
>>> v.x = 10             # 为v.x赋新的值,按理说应该会报异常
>>> v.x                # 读取v.x,返回来的是新的值
>>> v
Vector([0.0, 1.0, 2.0, 3.0, 4.0])# 数组并没有改变什么~

  如果为 .x 或 .y 实例属性赋值,会抛出 AttributeError。为了避免歧义,在 Vector 类中,如果为名称是单个小写字母的属性赋值,我们也想抛出那个异常。为此,我们要实现 __setattr__ 方法。

vector_v3.py 的部分代码:在 Vector 类中实现__setattr__ 方法

def __setattr__(self, name, value):
  cls = type(self)
  if len(name) == 1:           #特别处理名称是单个字符的属性
   if name in self.shorcut_name:       #如果name是xyzt中的一个,设置特殊的错误消息
    error = 'readonly attribute {attr_name!r}'
   elif name.islower():         #如果name是小写字母,为所有小写字母设置一个错误消息
    error = "can't set attributes 'a' to 'z' in {cls_name!r}"
   else:
    error = ''
   if error:            #否则,把错误消息设为空字符串
    msg = error.format(cls_name=cls.__name__, attr_name=name)
    raise AttributeError(msg)       #如果有错误消息,抛出 AttributeError
  super().__setattr__(name, value)       #默认情况:在超类上调用__setattr__方法,提供标准行为

Vector类第4版:散列和快速等值测试

   我们要再次实现 __hash__ 方法。加上现有的 __eq__ 方法,这会把Vector 实例变成可散列的对象。

    我们要使用^(异或)运算符依次计算各个分量的散列值,像这样:v[0] ^ v[1] ^ v[2]...。这正是functools.reduce 函数的作用。

归约函数(reduce、sum、any、all)把序列或有限的可迭代对象变成一个聚合结果

  我们已经知道 functools.reduce() 可以替换成 sum() ,下面说说它的原理。它的关键思想是,把一系列值归约成单个值。reduce() 函数的第一个参数是接受两个参数的函数,第二个参数是一个可迭代的对象。假如有个接受两个参数的 fn 函数和一个 lst 列表。调用reduce(fn, lst) 时,fn 会应用到第一对元素上,即 fn(lst[0],lst[1]) ,生成第一个结果 r1。然后,fn 会应用到 r1 和下一个元素上,即 fn(r1, lst[2]) ,生成第二个结果 r2。接着,调用 fn(r2,lst[3]) ,生成 r3……直到最后一个元素,返回最后得到的结果 rN。

举个🌰  使用 reduce 函数可以计算 5!(5 的阶乘):

>>> 2 * 3 * 4 * 5 # 想要的结果是:5! == 120
120
>>> import functools
>>> functools.reduce(lambda a,b: a*b, range(1, 6))
120

回到散列问题上。下面的 🌰 展示了计算聚合异或的 3 种方式:一种使用 for 循环,两种使用 reduce 函数。

>>> n = 0
>>> for i in range(1, 6):       # 使用for循环和累加器变量计算聚合异或
... n ^= i
...
>>> n
>>> import functools
>>> functools.reduce(lambda a, b: a^b, range(6))   # 使用reduce传入匿名函数
>>> import operator
>>> functools.reduce(operator.xor, range(6))  # 使用 functools.reduce 函数,把 lambda 表达式换成
operator.xor

给代码中添加__hash__方法

from array import array
import reprlib
import math
import numbers
import functools
import operator

class Vector:
 typecode = 'd'
 shorcut_name = 'xyzt'

 """
 省略的代码
 """

 def __eq__(self, other):
  return tuple(self) == tuple(other)

 def __hash__(self):
  hashes = (hash(x) for x in self._components)
  return functools.reduce(operator.xor, hashes, 0)   #把hashes提供给reduce函数,使用xor函数计算聚合的散列值;第三个参数,0 是初始值

注意:

  使用 reduce 函数时最好提供第三个参数,reduce(function, iterable, initializer) ,这样能避免这个异常:TypeError: reduce() of empty sequence with no initial value(这个错误消息很棒,说明了问题,还提供了解决方法)。如果序列为空,initializer 是返回的结果;否则,在归约中使用它作为第一个参数,因此应该使用恒等值。比如,对 +、| 和 ^ 来说, initializer 应该是 0;而对 * 和 & 来说,应该是 1。

实现的 __hash__ 方法是一种映射归约计算

映射归约:把函数应用到各个元素上,生成一个新序列(映射,map),然后计算聚合值(归约,reduce)

映射过程计算各个分量的散列值,归约过程则使用 xor 运算符聚合所有散列值。把生成器表达式替换成 map 方法,映射过程更明显:

  def __hash__(self):
   hashes = map(hash, self._components)
   return functools.reduce(operator.xor, hashes, 0)   #把hashes提供给reduce函数,使用xor函数计算聚合的散列值;第三个参数,0 是初始值

为了提高比较的效率,Vector.__eq__ 方法在 for循环中使用 zip 函数

def __eq__(self, other):
  if len(self) != len(other):         #如果对比的两个长度不一样,返回False
   return False
  for a, b in zip(self, other):        #如果两个对比Vector实例传递的值通过惰性对比,有不不一样的就返回FALSE
   if a != b:
    return False
  return True

zip的效率很好,不过用于计算聚合值的整个 for 循环可以替换成一行 all 函数调用:如果所有分量对的比较结果都是 True,那么结果就是 True。只要有一次比较的结果是 False,all 函数就返回False。使用 all 函数实现 __eq__ 方法的方式如下:

使用 zip 和 all 函数实现 Vector.__eq__ 方法,效果和上面的代码一样~

 def __eq__(self, other):
  return len(self) == len(other) and all(a == b for a, b in zip(self, other))

出色的 zip 函数

  使用 for 循环迭代元素不用处理索引变量,还能避免很多缺陷,但是需要一些特殊的实用函数协助。其中一个是内置的 zip 函数。使用 zip 函数能轻松地并行迭代两个或更多可迭代对象,它返回的元组可以拆包成变量,分别对应各个并行输入中的一个元素。

举个 🌰

>>> zip(range(3), 'abc')
<zip object at 0x1024d7b48>
>>> list(zip(range(3), 'abc'))              #zip返回一个生成器,按需生成元祖
[(0, 'a'), (1, 'b'), (2, 'c')]
>>> dict(zip('123', 'abc'))                #使用zip快速构造一个字典
{'1': 'a', '2': 'b', '3': 'c'}
>>> from itertools import zip_longest          #使用zip_longest可以给给个fillvalue设置一个默认值,当元祖迭代尽的时候可以补充默认值
>>> list(zip_longest(range(3), 'ABC', [0.0, 1.1, 2.2, 3.3], fillvalue=-1))
[(0, 'A', 0.0), (1, 'B', 1.1), (2, 'C', 2.2), (-1, -1, 3.3)]

Vector类第5版:格式化

Vector 类的 __format__ 方法与 Vector2d 类的相似,但是不使用极坐标,而使用球面坐标(也叫超球面坐标),因为 Vector 类支持 n 个维度,而超过四维后,球体变成了“超球体”。 因此,我们会把自定义的格式后缀由 'p' 变成 'h'。

下面几个示例摘自 vector_v5.py 的 doctest,是四维球面坐标格式:

>>> format(Vector([-1, -1, -1, -1]), 'h')
'<2.0, 2.0943951023931957, 2.186276035465284, 3.9269908169872414>'
>>> format(Vector([2, 2, 2, 2]), '.3eh')
'<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 1, 0, 0]), '0.5fh')
'<1.00000, 1.57080, 0.00000, 0.00000>'

vector_v5.py:Vector 类最终版的 doctest 和全部代码;带标号的那几行是为了支持 __format__ 方法而添加的代码

from array import array
import reprlib
import math
import numbers
import functools
import operator
import itertools

class Vector:
 typecode = 'd'

 def __init__(self, components):
  self._components = array(self.typecode, components)

 def __iter__(self):
  return iter(self._components)

 def __repr__(self):
  components = reprlib.repr(self._components)
  components = components[components.find('['):-1]
  return 'Vector({})'.format(components)

 def __str__(self):
  return str(tuple(self))

 def __bytes__(self):
  return (bytes([ord(self.typecode)]) + bytes(self._components))

 def __eq__(self, other):
  return (len(self) == len(other) and all(a == b for a, b in zip(self, other)))

 def __hash__(self):
  hashes = map(hash, self._components)
  return functools.reduce(operator.xor, hashes, 0)

 def __abs__(self):
  return math.sqrt(sum(x * x for x in self))

 def __bool__(self):
  return bool(abs(self))

 def __len__(self):
  return len(self._components)

 def __getitem__(self, index):
  cls = type(self)

  if isinstance(index, slice):
   return cls(self._components[index])
  elif isinstance(index, numbers.Integral):
   return self._components[index]
  else:
   msg = '{.__name__} indices must be integers'
   raise TypeError(msg.format(cls))

 shorcut_names = 'xyzt'

 def __getattr__(self, name):
  cls = type(self)

  if len(name) == 1:
   pos = cls.shorcut_names.find(name)
   if 0 <= pos < len(self._components):
    return self._components[pos]
  msg = '{.__name__!r} object has no attribute {!r}'
  raise AttributeError(msg.format(cls, name))

 def angle(self, n):
  r = math.sqrt(sum(x * x for x in self[n:]))
  a = math.atan2(r, self[n-1])
  if (n == len(self) - 1 ) and (self[-1] < 0):
   return math.pi * 2 - a
  else:
   return a

 def angles(self):
  return (self.angle(n) for n in range(1, len(self)))

 def __format__(self, fmt_spec=''):
  if fmt_spec.endswith('h'):
   fmt_spec = fmt_spec[:-1]
   coords = itertools.chain([abs(self)], self.angles())
   outer_fmt = '<{}>'
  else:
   coords = self
   outer_fmt = '({})'
  components = (format(c, fmt_spec) for c in coords)
  return outer_fmt.format(', '.join(components))

 @classmethod
 def frombytes(cls, octets):
  typecode = octets[0]
  memv = memoryview(octets[1:]).cast(typecode)
  return cls(memv)

测试的结果为:

"""
A multidimensional ``Vector`` class, take 5
A ``Vector`` is built from an iterable of numbers::

 >>> Vector([3.1, 4.2])
 Vector([3.1, 4.2])
 >>> Vector((3, 4, 5))
 Vector([3.0, 4.0, 5.0])
 >>> Vector(range(10))
 Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])

Tests with two dimensions (same results as ``vector2d_v1.py``)::

 >>> v1 = Vector([3, 4])
 >>> x, y = v1
 >>> x, y
 (3.0, 4.0)
 >>> v1
 Vector([3.0, 4.0])
 >>> v1_clone = eval(repr(v1))
 >>> v1 == v1_clone
 True
 >>> print(v1)
 (3.0, 4.0)
 >>> octets = bytes(v1)
 >>> octets
 b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
 >>> abs(v1)
 5.0
 >>> bool(v1), bool(Vector([0, 0]))
 (True, False)

Test of ``.frombytes()`` class method:

 >>> v1_clone = Vector.frombytes(bytes(v1))
 >>> v1_clone
 Vector([3.0, 4.0])
 >>> v1 == v1_clone
 True

Tests with three dimensions::

  > >> v1 = Vector([3, 4, 5])
 >>> x, y, z = v1
  >>> x, y, z
 (3.0, 4.0, 5.0)
 >>> v1
 Vector([3.0, 4.0, 5.  0])
 >>> v1_clone = eval(repr(v1))
 >>> v1 == v1_clone
 True
 >>> print(v1)
 (3.0, 4.0, 5.0)
 >> abs(v1) # doctest:+ELLIPSIS
 7.071067811...
 >>> bool(v1), bool(Vector([0, 0, 0]))
 (True, False)

Tests with many dimensions::

 >>> v7 = Vector(range(7))
 >>> v7
 Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
 >>> abs(v7) # doctest:+ELLIPSIS
 9.53939201..

Test of ``.__bytes__`` and ``.frombytes()`` methods::

 >>> v1 = Vector([3, 4, 5])
 >>> v1_clone = Vector.frombytes(bytes(v1))
 >>> v1_clone
 Vector([3.0, 4.0, 5.0])
 >>> v1 == v1_clone
 True

Tests of sequence behavior::

 >>> v1 = Vector([3, 4, 5])
 >>> len(v1)
 >>> v1[0], v1[len(v1)-1], v1[-1]
 (3.0, 5.0, 5.0)

Test of slicing::

 >>> v7 = Vector(range(7))
 >>> v7[-1]
 6.0
 >>> v7[1:4]
 Vector([1.0, 2.0, 3.0])
 >>> v7[-1:]
 Vector([6.0])
 >>> v7[1,2]
 Traceback (most recent call last):
 ...
 TypeError: Vector indices must be integers

Tests of dynamic attribute access::

 >>> v7 = Vector(range(10))
 >>> v7.x
 0.0
 >>> v7.y, v7.z, v7.t
 (1.0, 2.0, 3.0)

Dynamic attribute lookup failures::

 >>> v7.k
 Traceback (most recent call last):
 ...
 AttributeError: 'Vector' object has no attribute 'k'
 >>> v3 = Vector(range(3))
 >>> v3.t
 Traceback (most recent call last):
 ...
 AttributeError: 'Vector' object has no attribute 't'
 >>> v3.spam
 Traceback (most recent call last):
 ...
 AttributeError: 'Vector' object has no attribute 'spam'

Tests of hashing::

 >>> v1 = Vector([3, 4])
 >>> v2 = Vector([3.1, 4.2])
 >>> v3 = Vector([3, 4, 5])
 >>> v6 = Vector(range(6))
 >>> hash(v1), hash(v3), hash(v6)
 (7, 2, 1)

Most hash values of non-integers vary from a 32-bit to 64-bit CPython build::

 >>> import sys
 >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986)
 True

Tests of ``format()`` with Cartesian coordinates in 2D::

 >>> v1 = Vector([3, 4])
 >>> format(v1)
 '(3.0, 4.0)'
 >>> format(v1, '.2f')
 '(3.00, 4.00)'
 >>> format(v1, '.3e')
 '(3.000e+00, 4.000e+00)'

Tests of ``format()`` with Cartesian coordinates in 3D and 7D::

 >>> v3 = Vector([3, 4, 5])
 >>> format(v3)
 '(3.0, 4.0, 5.0)'
 >>> format(Vector(range(7)))
 '(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)'

Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D::

 >>> format(Vector([1, 1]), 'h') # doctest:+ELLIPSIS
 '<1.414213..., 0.785398...>'
 >>> format(Vector([1, 1]), '.3eh')
 '<1.414e+00, 7.854e-01>'
 >>> format(Vector([1, 1]), '0.5fh')
 '<1.41421, 0.78540>'
 >>> format(Vector([1, 1, 1]), 'h') # doctest:+ELLIPSIS
 '<1.73205..., 0.95531..., 0.78539...>'
 >>> format(Vector([2, 2, 2]), '.3eh')
 '<3.464e+00, 9.553e-01, 7.854e-01>'
 >>> format(Vector([0, 0, 0]), '0.5fh')
 '<0.00000, 0.00000, 0.00000>'
 >>> format(Vector([-1, -1, -1, -1]), 'h') # doctest:+ELLIPSIS
 '<2.0, 2.09439..., 2.18627..., 3.92699...>'
 >>> format(Vector([2, 2, 2, 2]), '.3eh')
 '<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>'
 >>> format(Vector([0, 1, 0, 0]), '0.5fh')
 '<1.00000, 1.57080, 0.00000, 0.00000>'
"""

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 深入解析Python中的list列表及其切片和迭代操作

    有序列表list >>> listTest = ['ha','test','yes'] >>> listTest ['ha', 'test', 'yes'] len()获取list元素个数. >>> len(listTest) 3 可以用索引来访问每一个元素,0表示第一个,-1还可以表示最后一个,即倒数第一个,依此类推-2表示倒数第二个,超过了也会报越界错误. >>> listTest[0] 'ha' >>> lis

  • Python字符串切片操作知识详解

    一:取字符串中第几个字符 print "Hello"[0] 表示输出字符串中第一个字符 print "Hello"[-1] 表示输出字符串中最后一个字符 二:字符串分割 print "Hello"[1:3] #第一个参数表示原来字符串中的下表 #第二个阐述表示分割后剩下的字符串的第一个字符 在 原来字符串中的下标 这句话说得有点啰嗦,直接看输出结果: el 三:几种特殊情况 (1)print "Hello"[:3] 从第一个字

  • Python切片知识解析

    切片原型 strs = 'abcdefg' Strs[start: end:step] 切片的三个参数分别表开始,结束,步长 第一位下标为0,end位不取,如strs[1:3] = 'bc' 如果start,end超出现有数组范围,按实际范围截断strs[-100:100]='abcdefg' Step为空时,缺省值为1 Strs[1:5] = 'bcde' strs[1:5:2] = 'bd' Step为正时,start<end,否则为空 Strs[5:1] = '' Start为空,默认为负

  • 通过源码分析Python中的切片赋值

    本文主要介绍的关于Python切片赋值的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 昨天有同学问了我这么个问题: t = [1, 2, 3] t[1:1] = [7] # 感谢@一往直前 的疑问,之前写为 t[1:1] = 7了 print t # 输出 [1, 7, 2, 3] 这个问题之前还真没遇到过,有谁会对列表这么进行赋值吗?不过对于这个输出结果的原因确实值得去再了解下,毕竟之前也看过<Python源码分析>.(题外话:据说最近有大牛在写新的版本) 想着今天有空看看P

  • Python切片用法实例教程

    本文以实例形式讲述了Python中切片操作的用法,分享给大家供大家参考借鉴,具体如下: 取一个list或tuple的部分元素是非常常见的操作.比如,一个list如下: >>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] 取前3个元素,应该怎么做呢? 比较笨的办法如下: >>> [L[0], L[1], L[2]] ['Michael', 'Sarah', 'Tracy'] 之所以是笨办法是因为扩展一下,取前N个元素就

  • 彻底理解Python list切片原理

    关于list的insert函数 list#insert(ind,value)在ind元素前面插入value 首先对ind进行预处理:如果ind<0,则ind+=len(a),这样一来ind就变成了正数下标 预处理之后, 当ind<0时,ind=0,相当于头部插入  当ind>len(a)时,ind=len(a),相当于尾部插入 切片实例 Python中的列表切片非常灵活,要根据表象来分析它的内在机理,这样用起来才能溜. 下标可以为负数有利有弊,好处是使用起来更简便,坏处是当我下表越界了我

  • Python列表切片用法示例

    本文实例讲述了Python列表切片用法.分享给大家供大家参考,具体如下: Python中符合序列的有序序列都支持切片(slice),例如列表,字符串,元组. 格式:[start:end:step] start:起始索引,从0开始,-1表示结束 end:结束索引 step:步长,end-start,步长为正时,从左向右取值.步长为负时,反向取值 注意切片的结果不包含结束索引,即不包含最后的一位,-1代表列表的最后一个位置索引 a=[1,2,3,4,5,6] b1=a[:] #省略全部,代表截取全部

  • python 切片和range()用法说明

    理解切片基本用法: 首先需要明白,可迭代对象,按照正数索引(正序)是从0开始的,按照负数索引(逆序)是从-1开始的.>>> astring = 'Hello world'>>> astring[0:2]'He'>>> 可见,这种情况下,给切片操作一个起始位置,和一个终止位置,则显示从起始位置开始(包括起始位置)到终止位置(不包括终止位置)之间的内容: 在有负数索引的情况下,是类似的,只要确定终止位置的内容: >>> astring[0

  • Python中序列的修改、散列与切片详解

    前言 本文主要给大家介绍了关于Python中序列的修改.散列与切片的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. Vector类:用户定义的序列类型 我们将使用组合模式实现 Vector 类,而不使用继承.向量的分量存储在浮点数数组中,而且还将实现不可变扁平序列所需的方法. Vector 类的第 1 版要尽量与前一章定义的 Vector2d 类兼容. Vector类第1版:与Vector2d类兼容 Vector 类的第 1 版要尽量与前一章定义的 Vector2d

  • 对python中的控制条件、循环和跳出详解

    对python中的控制条件.循环和跳出详解 代码缩进(代码块): python用缩进表示代码块,没有其他语言的大括号 缩进是强制检查,整个代码缩进必须一致,否则无法运行 用2.4个空格或者tab缩进 ide自动保证缩进一致 If.elif和else的条件分支: if if...else if...elif..else 没有switch.case语法 空的列表.元祖.字符串.0都被评估为False None被评估为False 控制条件后面必须加":" a=100 if a > 80

  • 对Python中class和instance以及self的用法详解

    一. Python 的类和实例 在面向对象中,最重要的概念就是类(class)和实例(instance),类是抽象的模板,而实例是根据类创建出来的一个个具体的 "对象". 就好比,学生是个较为抽象的概念,同时拥有很多属性,可以用一个 Student 类来描述,类中可定义学生的分数.身高等属性,但是没有具体的数值.而实例是类创建的一个个具体的对象, 每一个对象都从类中继承有相同的方法,但是属性值可能不同,如创建一个实例叫 hansry 的学生,其分数为 93,身高为 176,则这个实例拥

  • Python中zip()函数的解释和可视化(实例详解)

    zip()的作用 先看一下语法: zip(iter1 [,iter2 [...]]) -> zip object Python的内置help()模块提供了一个简短但又有些令人困惑的解释: 返回一个元组迭代器,其中第i个元组包含每个参数序列或可迭代对象中的第i个元素.当最短的可迭代输入耗尽时,迭代器将停止.使用单个可迭代参数,它将返回1元组的迭代器.没有参数,它将返回一个空的迭代器. 与往常一样,当您精通更一般的计算机科学和Python概念时,此模块非常有用.但是,对于初学者来说,这段话只会引发更

  • python中requests库session对象的妙用详解

    在进行接口测试的时候,我们会调用多个接口发出多个请求,在这些请求中有时候需要保持一些共用的数据,例如cookies信息. 妙用1 requests库的session对象能够帮我们跨请求保持某些参数,也会在同一个session实例发出的所有请求之间保持cookies. 举个栗子,跨请求保持cookies,在命令行上输入下面命令: # 创建一个session对象 s = requests.Session() # 用session对象发出get请求,设置cookies s.get('http://ht

  • 基于python中pygame模块的Linux下安装过程(详解)

    一.使用pip安装Python包 大多数较新的Python版本都自带pip,因此首先可检查系统是否已经安装了pip.在Python3中,pip有时被称为pip3. 1.在Linux和OS X系统中检查是否安装了pip 打开一个终端窗口,并执行如下命令: Python2.7中: zhuzhu@zhuzhu-K53SJ:~$ pip --version pip 8.1.1 from /usr/lib/python2.7/dist-packages (python 2.7) Python3.X中: z

  • 在python中按照特定顺序访问字典的方法详解

    最近使用python写一些东西,在参考资料的时候发现字典是没有顺序的,那么怎么样按照一定顺序访问字典呐,我找到了一个小方法: 假设一个字典是: D = {'a': '1', 'b': '2', 'c': '3'} 如果我们要按照a, b, c的顺序访问字典,可以借助一个列表,比如说: L = list(D.keys()) L.sort() for key in L: print(key, 'is' D[key]) 输出为: a is 1 b is 2 c is 3 需要倒序的话只需使用倒序函数排

  • 对python中的os.getpid()和os.fork()函数详解

    如下所示: import os import sys import time processNmae = 'parent' print "Program executing ntpid:%d,processNmae:%s"%(os.gitpid(),processNmae) #attempt to fork child process try: forkPid = os.fork() except OSError: sys.exit("Unable to create new

  • 对python中url参数编码与解码的实例详解

    一.简介 在python中url,对于中文等非ascii码字符,需要进行参数的编码与解码. 二.关键代码 1.url编码 对字符串编码用urllib.parse包下的quote(string, safe='/', encoding=None, errors=None)方法. 对json格式的参数名和值编码,用urllib.parse包下的 urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote_via=qu

  • 对Python 中矩阵或者数组相减的法则详解

    最近在做编程练习,发现有些结果的值与答案相差较大,通过分析比较得出结论,大概过程如下: 定义了一个计算损失的函数: def error(yhat,label): yhat = np.array(yhat) label = np.array(label) error_sum = ((yhat - label)**2).sum() return error_sum 主要出现问题的是 yhat - label 部分,要强调的是一定要保证两者维度是相同的!这点很重要,否则就会按照python的广播机制进

随机推荐