zoukankan      html  css  js  c++  java
  • Python数据模型

    引言

    像大多数人一样,我在对一直传统的面向过程语言C一知半解之后,走进了面向对象的世界,尽管对OOP一无所知,还好Python还保留有函数式编程,这使得我才不那么抵触,直到现在,习惯了面向对象之后,也习惯了接口这些叫法,而不是函数。

    在看到len(collection)与collection.len(),也越来越习惯后者,他所代表的强大的思想,(其实是调用的collection对象的内部__len__方法),这种设计思想完全体现在 Python 的数据模型上,而数据模型所描述的 API,为使用最地道的语言特性来构建你自己的

    对象提供了工具。数据模型其实是对 Python 框架的描述,它规范了这门语言自身构建模块的接口,这些模块包括但不限于序列、迭代器、函数、类等。

    一、私有和被保护的属性

    类的私有属性:

    __private_attrs:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。
    在类内部的方法中使用时  self.__private_attrs。

    类的方法:

    在类地内部,使用def关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数

    类的私有方法 :

    __private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。
    在类的内部调用 self.__private_methods

    默认情况下,Python中的成员函数和成员变量都是公开的(public),在python中没有类似public,private等关键词来修饰成员函数和成员变量。
    在python中定义私有变量只需要在变量名或函数名前加上 ”__“两个下划线,那么这个函数或变量就是私有的了。
    在内部,python使用一种 name mangling 技术,将 __membername替换成 _classname__membername,也就是说,类的内部定义中,
    所有以双下划线开始的名字都被"翻译"成前面加上单下划线和类名的形式。

         例如:为了保证不能在class之外访问私有变量,Python会在类的内部自动的把我们定义的__spam私有变量的名字替换成为
    _classname__spam(注意,classname前面是一个下划线,spam前是两个下划线),因此,用户在外部访问__spam的时候就会
    提示找不到相应的变量。   python中的私有变量和私有方法仍然是可以访问的;访问方法如下:

    私有变量:实例._类名__变量名
    私有方法:实例._类名__方法名()
     1 class people():
     2     __place = "nanjing"
     3     _age1 = 20
     4 
     5     def __init__(self, name):
     6         self.name = name
     7 
     8     def __sayhello(self):
     9         print("%s say hello" % self.name)
    10 
    11 class teacher(people):
    12     pass
    13 
    14 t1 = teacher("cmz")
    15 print(t1._people__place)  # 访问私有变量
    16 t1._people__sayhello()    # 访问私有方法
    17 
    18 结果是
    19 nanjing
    20 cmz say hello
    21 
    22 python私有属性和方法案例
    View Code

    其实,Python并没有真正的私有化支持,但可用下划线得到伪私有。   尽量避免定义以下划线开头的变量!

    (1)_xxx      "单下划线" 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量,
                   需通过类提供的接口进行访问;不能用'from module import *'导入
    (2)__xxx    类中的私有变量/方法名 (Python的函数也是对象,所以成员方法称为成员变量也行得通。),
                  " 双下划线 " 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
    (3)__xxx__ 系统定义名字,前后均有一个“双下划线” 代表python里特殊方法专用的标识,如 __init__()代表类的构造函数。
     1 class people():
     2     __place = "nanjing"
     3     _age = 20
     4 
     5     def __init__(self, name):
     6         self.name = name
     7 
     8     def _test(self):
     9         print("from people test")
    10 
    11     def __sayhello(self):
    12         print("%s say hello" % self.name)
    13 
    14 class teacher(people):
    15     pass
    16 
    17 t1 = teacher("cmz")
    18 print(t1._age)
    19 print(people._age)
    20 t1._test()
    21 people._test(t1) # 传入对象t1
    22 
    23 结果是
    24 20
    25 20
    26 from people test
    27 from people test
    View Code
     1 class people():
     2     __place = "nanjing"
     3     _age = 20
     4 
     5     def __init__(self, name):
     6         self.name = name
     7 
     8     def __sayhello(self):
     9         print("%s say hello" % self.name)
    10 
    11 class teacher(people):
    12     pass
    13 
    14 t1 = teacher("cmz")
    15 print(t1._people__place)
    16 t1._people__sayhello()
    17 
    18 结果是
    19 nanjing
    20 cmz say hello
    View Code

    二、搭建一摞pythonic的纸牌

    python的另一强大之处就是丰富的标准库,还有许许多多的第三方库,这使得不用重复造轮子

    import collections
    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 suit in self.suits
                  for rank in self.ranks]
      def __len__(self):
        return len(self._cards)
      def __getitem__(self, position):
        return self._cards[position]

    deck = FrenchDeck()
    for i in deck[:10]:  # 其实这里调用的是deck这个可迭代对象背后其实用的是 iter(x),而这个函数的背后则是 x.__iter__() 方法

      print(i)

    #打印十张纸牌
    Card(rank='2', suit='spades')
    Card(rank='3', suit='spades')
    Card(rank='4', suit='spades')
    Card(rank='5', suit='spades')
    Card(rank='6', suit='spades')
    Card(rank='7', suit='spades')
    Card(rank='8', suit='spades')
    Card(rank='9', suit='spades')
    Card(rank='10', suit='spades')
    Card(rank='J', suit='spades')
    # 对纸牌进行排序

    suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) def spades_high(card): rank_value = FrenchDeck.ranks.index(card.rank) return rank_value * len(suit_values) + suit_values[card.suit] 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')

      

    三、特殊方法

    下面来看看特殊方法

    beer_card = Card('7', 'diamonds')
    >>> beer_card
    Card(rank='7', suit='diamonds')

    len()方法与特殊方法__len__,

    特殊方法的存在是为了被 Python 解释器调用的,你自己并不需要调用它们。也就是说没有 my_object.__len__() 这种写法,而应该使用 len(my_object)。在执行 len(my_object) 的时候,如果my_object 是一个自定义类的对象,那么 Python 会自己去调用其中由

    你实现的 __len__ 方法。abs也是同理,

    如果是 Python 内置的类型,比如列表(list)、字符串(str)、字节序列(bytearray)等,那么 CPython 会抄个近路,__len__ 实际上会直接返回 PyVarObject 里的 ob_size 属性。PyVarObject 是表示内存中长度可变的内置对象的 C 语言结构体。直接读取这

    个值比调用一个方法要快很多。

    >>> deck = FrenchDeck()
    >>> len(deck)
    52

    从一叠牌中抽取特定的一张纸牌,比如说第一张或最后一张,是很容易的:deck[0] 或 deck[-1]。这都是由 __getitem__ 方法提供的

    字典中也有这种用法,类似dic[k], 其背后也是__getitem__在默默支持,不过这里返回的值而是键k所对应的值value

    >>> deck[0]
    Card(rank='2', suit='spades')
    >>> deck[-1]
    Card(rank='A', suit='hearts'

    3.1、运算符重载

    说到特殊方法来看一下运算符重载,运算符重载只是意味着在类方法中拦截内置的操作——当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果。

    # Number类提供了一个方法来拦截实例的构造函数(__init__),此外还有一个方法捕捉减法表达式(__sub__)。这种特殊的方法是钩子,可与内置运算绑定。
    >>> class Number: def __init__(self,start): self.data = start def __sub__(self,other): return Number(self.data - other) >>> X = Number(5) >>> X.data 5 >>> Y = X - 2 >>> Y.data 3

     3.2、 方法重载

    overloading method:是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。重载是让类以统一的方式处理不同类型数据的一种手段。
    函数重载主要是为了解决两个问题。
    1.可变参数类型。
    2.可变参数个数。

    另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
     
    对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。

    对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。

    鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。

    子类不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写(overriding method)又称方法覆盖。

     

    3.3、常见的特殊方法

    当然也可以使用dir内置函数来查看常见并比较的数据结构的特殊方法,如list,dict等。

    dir(list)
    ['__add__',
     '__class__',
     '__contains__',
     '__delattr__',
     '__delitem__',
     '__dir__',
     '__doc__',
     '__eq__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__getitem__',
     '__gt__',
     '__hash__',
     '__iadd__',
     '__imul__',
     '__init__',
     '__iter__',
     '__le__',
     '__len__',
     '__lt__',
     '__mul__',
     '__ne__',
     '__new__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__reversed__',
     '__rmul__',
     '__setattr__',
     '__setitem__',
     '__sizeof__',
     '__str__',
     '__subclasshook__',
     'append',
     'clear',
     'copy',
     'count',
     'extend',
     'index',
     'insert',
     'pop',
     'remove',
     'reverse',
     'sort']
    View Code
    dir(tuple)
    ['__add__',
     '__class__',
     '__contains__',
     '__delattr__',
     '__dir__',
     '__doc__',
     '__eq__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__getitem__',
     '__getnewargs__',
     '__gt__',
     '__hash__',
     '__init__',
     '__iter__',
     '__le__',
     '__len__',
     '__lt__',
     '__mul__',
     '__ne__',
     '__new__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__rmul__',
     '__setattr__',
     '__sizeof__',
     '__str__',
     '__subclasshook__',
     'count',
     'index']
    View Code
  • 相关阅读:
    使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)
    便携版WinSCP在命令行下同步文件夹
    ffmpeg (ffprobe)分析文件关键帧时间点
    sqlite删除数据或者表后,回收数据库文件大小
    ubuntu 20.04下 freeswitch 配合 fail2ban 防恶意访问
    ffmpeg使用nvenc编码的结论记录
    PC版跑跑卡丁车 故事模式 亚瑟传说章节 卡美洛庆典 2阶段 心灵之眼 攻略
    There was an error loading or playing the video
    Nvidia RTX Voice 启动报错修复方法
    火狐浏览器 关闭跨域限制
  • 原文地址:https://www.cnblogs.com/welan/p/9419230.html
Copyright © 2011-2022 走看看