zoukankan      html  css  js  c++  java
  • 《流畅的Python》学习笔记1(第1章:Python数据模型)

    1.1 一摞Python风格的纸牌

    __getitem____len__

    例1,一摞有序的纸牌

    import collections
    
    # namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素,
    # 并可以用属性而不是索引来引用tuple的某个元素。
    # namedtuple用来构建只有少数属性但是没有方法的对象
    Card = collections.namedtuple('Card',['rank','suit'])
    
    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 rank in self.ranks
                                           for suit in self.suits]
        def __len__(self):
            return len(self._cards)
        def __getitem__(self, position):
            return self._cards[position]
    # 利用namedtuple会轻松得到一个纸牌对象 beer_card = Card('7','diamonds') print(beer_card) # 用len()函数来查看一叠牌有多少张 deck = FrenchDeck() print(len(deck)) # 利用__getitem__方法从一叠牌中抽取特定的一张纸牌,比如第一张或最后一张。 print(deck[0]) print(deck[-1]) # Python内置从一直序列中随机选出一个元素的函数random.choice。 from random import choice print("随机抽取一张牌3次:") print(choice(deck)) print(choice(deck)) print(choice(deck)) # 查看一摞牌最上面3张和只看牌面是A的牌的操作 # 其中第二种操作的具体方法是,先抽出索引12的那张牌,然后每向后数13张牌拿一张 print("最上面3张牌:") print(deck[:3]) print("牌面为A的牌:") print(deck[12::13])

    输出结果:

    Card(rank='7', suit='diamonds')
    52
    Card(rank='2', suit='spades')
    Card(rank='A', suit='hearts')
    随机抽取一张牌3次:
    Card(rank='9', suit='clubs')
    Card(rank='J', suit='clubs')
    Card(rank='10', suit='diamonds')
    最上面3张牌:
    [Card(rank='2', suit='spades'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='clubs')]
    牌面为A的牌:
    [Card(rank='5', suit='spades'), Card(rank='8', suit='diamonds'), Card(rank='J', suit='clubs'), Card(rank='A', suit='hearts')]

    为了能够清晰的看出每一步得到的结果,也不需要这么多的输出语句,(注意注释掉上面暗调的代码)可在控制台运行:

    >>> from frenchdeck import FrenchDeck, Card
    >>> beer_card = Card('7','diamonds')
    >>> beer_card
    Card(rank='7', suit='diamonds')
    >>> deck = FrenchDeck()
    >>> len(deck)
    52
    >>> deck[0]
    Card(rank='2', suit='spades')
    >>> deck[-1]
    Card(rank='A', suit='hearts')
    >>> from random import choice
    >>> choice(deck)
    Card(rank='2', suit='hearts')
    >>> choice(deck)
    Card(rank='K', suit='hearts')
    >>> choice(deck)
    Card(rank='J', suit='spades')
    >>> deck[:3]
    [Card(rank='2', suit='spades'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='clubs')]
    >>> deck[12::13]
    [Card(rank='5', suit='spades'), Card(rank='8', suit='diamonds'), Card(rank='J', suit='clubs'), Card(rank='A', suit='hearts')]
    >>>

    给一摞扑克牌排序,按照2最小,A最大;黑桃最大,红桃次之,方块再次,梅花最小。

    按照这个规则给扑克牌排序,梅花2大小是0,黑桃A是51

    suit_values = dict(spades =3, hearts=2, diamonds=1, clubs=0)
    def spades_high(card):
        # 在[2,3,4,5,6,7,8,9,10,J,Q,K,A]中的位置
        rank_value = FrenchDeck.ranks.index(card.rank)
        # suit_values[card.suit]加上花色的大小
        return rank_value*len(suit_values)+suit_values[card.suit]
    deck = FrenchDeck()
    for card in sorted(deck, key=spades_high):
        print(card)
    Card(rank='2', suit='clubs')
    Card(rank='2', suit='diamonds')
    Card(rank='2', suit='hearts')
    ...(46 cards ommitted)
    Card(rank='A', suit='diamonds')
    Card(rank='A', suit='hearts')
    Card(rank='A', suit='spades')

    一般注记:按照目前的设计,FenchDeck是不能洗牌的,因为这摞牌是不可变的:卡牌和它们的位置都是固定的,除非破坏这个类的封装性,直接对_cards进行操作。第11章会讲到。

    1.2 如何使用特殊方法

    首先明确一点,特殊方法的存在是为了被Python解释器调用的,你自己并不需要调用它们。也就是说没有my_object.__len__()这种写法,而应该使用len(my_object)。

    通常你的代码无需直接使用特殊方法。除非有大量的元编程存在,直接调用特殊方法的频率远远低于你去实现它们的次数。唯一的例外可能是__init__方法。

    1.2.1 模拟数值类型

    我们来实现一个二维向量(vector)类,这里的向量就是欧几里得几何中常用的概念。

    __repr__、__abs__、__add__和__mul__

    例2,一个简单的二维向量类

    from math import hypot
    
    class Vector:
    
        def __init__(self, x=0, y=0):
            self.x = x
            self.y = y
        def __repr__(self):
            return 'Vector(%r, %r)' % (self.x, self.y)
        def __abs__(self):
            return hypot(self.x, self.y)
        def __bool__(self):
            return bool(abs(self))
        def __add__(self, other):
            x = self.x + other.x
            y = self.y + other.y
            return Vector(x,y)
        def __mul__(self, scalar):
            return Vector(self.x * scalar,self.y * scalar)

    为了给这个类设计API,我们先写个模拟的控制台会话来做doctest。

    >>> from vector import Vector
    >>> v1 = Vector(2,4)
    >>> v2 = Vector(2,1)
    >>> v1 + v2
    Vector(4, 5)
    >>> v = Vector(3,4)
    >>> abs(v)
    5.0
    >>> v * 3
    Vector(9, 12)
    >>> abs(v * 3)
    15.
    >>> v * -3
    Vector(-9, -12)

    abs是一个内置函数,如果输入是整数或者浮点数,它返回的是输入值的绝对值;如果输入是复数,那么返回这个复数的摸。

    *运算符实现向量的标量乘法(即向量与数的乘法,得到的结果向量的方向与原向量方向一致,模变大。如果向量与负数相乘,得到的结果向量的方向与原向量相反。)

    1.2.2 字符串表示形式

    Python有一个内置的函数叫repr,它能把对一个对象用字符串的形式表达出来以便辨认,这就是“字符串表示形式”。

    %和str.format是两种格式化字符串的手段。

    __str__和__repr__的区别(https://stackoverflow.com/questions/1436703/difference-between-str-and-repr

    1.2.3 算术运算符

    通过例2发现,__add__和__mul__两个方法的返回值都是新创建的向量对象,被操作的两个向量(self或other)还是原封不动,代码里只是读取了它们的值而已。中缀运算符的基本原则就是不改变操作对象,而是产出一个新的值。

    1.2.4 自定义的布尔值

    如果想让Vector.__bool__更高效,可以采用:

        def __bool__(self):
            return bool(self.x or self.y)

    它不那么易读,却能省掉从abs到__abs__到平方再到平方根这些中间步骤。通过bool把返回类型显示转换为布尔值是为了符合__bool__对返回值的规定,因为or运算符可能会返回x或者y本身的值:若x的值等价于真,则or返回x的值;否则返回y的值。

    1.3 特殊方法一览

    表1-1:跟运算符无关的特殊方法

    类别 方法名
    字符串字节序列表示形式 _ repr _ , _ format _ , _ str _ , _ bytes _
    数值转换 _ abs _ , _ bool _ , _ complex _ , _ int _ ,_ float _ , _ hash _ , _ index _
    集合模拟 _ len _ , _ getitem _ , _ setitem _ , _ delitem _ ,_ contains _
    迭代枚举  iter _ , _ reversed _ , _ next _
    可调用模拟 _ call _
    上下文管理 _ enter _ , _ exit _
    实例创建和销毁 _ new _ , _ init _ , _ del _
    属性管理 _ getattr _ , _ getattribute _ , _ setattr _ , _ delattr _ , _ dir _
    属性描述符 _ get _ , _ set _ , _ delete _
    跟踪相关的服务 _ prepare _ , _ instancecheck _ , _ subclasscheck _

    表1-2:跟运算符相关的特殊方法

    类别 方法名和对应的运算符
    一元运算符  neg _ -, _ pos _ +, _ abs _ abs( )
    比较运算符 _ lt _ <, _ le _ <=, _ eq _ ==, _ ne _ !=, _ gt _ > , _ ge _ >=,
    算术运算符 _ add _ + , _ sub _ - , _ mul _ * , _ truediv _ / , _ floordiv _ // 
    _ mod _ % , _ divmod _ divmode() , _ pow _ **或pow() _ round _ round()
    反向算数运算符  _ radd _ , _ rsub _ , _ rmul _ , _ rtruediv _ , _ rfloordiv _ 
    _ rmod _ , _ rdivmod _ , _ rpow _
    增量赋值运算符 _ iadd _ , _ isub _ , _ imul _ , _ itruediv _ , _ ifloordiv _ 
    _ imod _ , _ idivmod _ , _ ipow _
    位运算符 _ invert _ ~, _ lshift _ <<, _ rshift _ >>, _ adn _ &, _ or _
    反向位运算符 _ rlshift _ , _ rrshift _ , _ rand _ , _ rxor _ , _ ror _
    增量赋值位运算符 _ ilshift _ , _ irshift _ , _ iand _ , _ ixor _ , _ ior _

    提示:当交换两个操作数的位置时,就会调用反向运算符(b*a而不是a*b)

  • 相关阅读:
    Realtime crowdsourcing
    maven 常用插件汇总
    fctix
    sencha extjs4 command tools sdk
    首次吃了一颗带奶糖味的消炎药,不知道管用不
    spring mvc3 example
    ubuntu ati driver DO NOT INSTALL recommand driver
    yet another js editor on windows support extjs
    how to use springsource tools suite maven3 on command
    ocr service
  • 原文地址:https://www.cnblogs.com/cathycheng/p/11353224.html
Copyright © 2011-2022 走看看