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)

  • 相关阅读:
    实现简单高效的网址(文本)缩短服务
    Xsl模板应用基础(三、模板嵌套)
    Xsl模板应用基础(二、程序转换)
    Xsl模板应用基础(一、介绍)
    C#调用360杀毒软件查杀指定路径
    微信公众平台网页授权获取用户基本信息(VS.NET版)
    微信公众平台开发者模式自动回复功能与自定义菜单
    微信公众平台接入
    Git常用操作整理
    JVM知识点总结(一)——运行时数据区
  • 原文地址:https://www.cnblogs.com/cathycheng/p/11353224.html
Copyright © 2011-2022 走看看