Python数据模型

Posted 蔚蓝的蓝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于Python数据模型的主要内容,如果未能解决你的问题,请参考以下文章

《Python机器学习及实践》----监督学习经典模型

《Python机器学习及实践》----模型实用技巧

《Python机器学习及实践》----模型实用技巧

python 用于数据探索的Python代码片段(例如,在数据科学项目中)

python常用代码

AJAX相关JS代码片段和部分浏览器模型