切片索引方式class C:
def __getitem__(self,index):
print(index)
c = C()
c[2]
c[2:7]
注意:
class D:
def __init__(self,data):
self.data = data
def __getitem__(self,index):
return self.data[index]*2
d = D([1,2,3,4])
for i in d:
print(i,end=' ')#2 4 6 8
s = 'hello world ! ni hao '
# 等价于
print(s[slice(2,7)])
print(s[7:])
print(s[slice(7,None)])
class D:
def __init__(self,data):
self.data = data
def __getitem__(self,index):
return self.data[index]
def __setitem__(self,index,value):
self.data[index] = value
d = D([1,2,3,4])
print(d[1])
d[1] = 6
print(d[1])
对象定义了__ iter __ () 魔法方法,那么它就是一个可迭代对象;如果一个可迭代对象定义了__ next() __ 魔法方法,那么它就是一个迭代器list1 =[1,2,3,4,5]
# 第一步先调用iter()获取迭代器
_ = iter(list1)
# 第二步使用__next__()进行迭代操作
while True:
try:
i = _.__next__()
except StopIteration:
break
print(i,end=' ')
应用:自己创建一个迭代器:class Double:
def __init__(self,start,stop):
self.value = start -1
self.stop = stop
def __iter__(self):
# __iter_-方法返回的是一个迭代器
return self
def __next__(self):
if self.value == self.stop:
raise StopIteration
self.value += 1
return self.value*2
d = Double(1,5)
for i in d:
print(i,end=' ')
在介绍容器的魔法方法之前,要首先知道python中常见的容器类型:
容器协议.1、协议(Protocols)与其他编程语言中的接口恒相似,它规定你哪些地方必须要定义,然而在python中的协议就显得不那么正式,事实上,在python中,协议更像是一种指南。
2、序列类型和映射类型(元组,字典,字符串,列表)等都属于容器类型,关于容器类型的协议如下:
(1)如果你希望定制的容器是不可变的话(元组,字符串等),你只需定义__len__()和__getitem__()方法;
(2)如果你希望定制的容器是可变的话,你除了定义__len__()和__getitem__()方法之外,还需要定义__setitem__()和__delitem__()方法。
| 魔法方法名 | 作用 | 备注 |
|---|---|---|
| __ contains __(self,item) | 主要用于成员关系的监测,对应的运算符是 in 和 not in | 当使用了in 或者 not in 但又没有实现__ contains __(),就会使用 __ iter __()和 __ next __()作为代偿的魔法方法 |
| __ getitem __(self, key) | 定义获取容器中指定元素的操作 ,相当于 self[key]; | 当找不到__ iter ()和 next ()方法时,就会去找 getitem __()方法,起到代偿的作用 |
| __ setitem__(self, key, value) | 定义设置容器中指定元素的操作 | 相当于 self[key] = value |
| __ delitem__(self, key) | 定义删除容器中指定元素的操作 | 相当于 del self[key]; |
| __ len__(self) | 定义当被 len() 调用时的操作(返回容器中元素的个数) | 无 |
| __ iter__(self) | 定义迭代容器中的元素的操作; | 无 |
| __ reversed__(self) | 定义当被 reversed() 调用时的操作 | 无 |
基本用法代码示例:
# 自己实现一个list
class Mylist:
#自己设计一个list
def __init__(self,values = None):
self.values = values
self.index = 0
def __setitem__(self,index,value):
self.values[index] = value
def __getitem__(self,index):
return self.values[index]
def __delitem__(self,index):
del self.values[index]
def __len__(self):
return len(self.values)
def __iter__(self):
#将实例生成可迭代对象
return self
def __next__(self):
#迭代实现的具体细节
#如果__iter__返回的是self,就必须实现该方法.因为此时self是生成器,必须由next()方法
if self.index >= len(self.values):
raise StopIteration()
value = self.values[self.index]
self.index += 1
return value
def __contains__(self,value):
return value in self.values
def __reversed__(self):
#反转
return list(reversed(self.values))
def __str__(self):
#return "This is my own List!"
return str(self.values)
def __repr__(self):
return self.values
mylist = Mylist([1,2,3,4,5,6])
print(mylist)
print(mylist[0])
mylist[0] = 7#__setitem__
print(mylist[0])#getitem
print(1 in mylist)#__contains__
print(len(mylist))#__len__
print([i for i in mylist])#__iter__
print(mylist)
f = reversed(mylist)#__reversed__
print(f)
代偿代码示例:
class C:
def __init__(self,data):
self.data = data
def __contains__(self,item):
return item in self.data
c = C([1,2,3,4,5,6])
print(3 in c)
print(0 in c)
# 当__contains__不存在时,会找__ iter __和__next__
class C:
def __init__(self,data):
self.data = data
def __iter__(self):
print('Iter',end= "->")
self.i = 0
return self
def __next__(self):
print("next",end = "->")
if self.i == len(self.data):
raise StopIteration
item = self.data[self.i]
self.i += 1
return item
c = C([1,2,3,4,5,6])
print(3 in c)
# 当__contains__不存在时,__ iter __和__next__也不存在时,会找getitem方法
class C:
def __init__(self,data):
self.data = data
def __getitem__(self,index):
print("GetItem",end='->')
return self.data[index]
c = C([1,2,3,4,5,6])
print(3 in c)
关于类的表示相关的魔法方法,主要包括__str__()和__repr__()两种。这两个方法都是用来描述类或对象信息的,比如你直接实例化了一个对象,打印出来的是这个对象的地址。而要是重新在类中定义了这两个方法,那打印对象的结果就是方法返回的信息。
# class D:
# def __init__(self):
# super().__init__()
# d = D()
# print(d)#<__main__.D object at 0x000002142D900CA0>
# 如果在类中定义了__str__()和__repr__()方法,那打印对象就是输出该方法的返回信息
class D:
def __init__(self):
super().__init__()
def __repr__(self):
return '我也是描述对象状态信息的魔法方法,但我是给解释器看的,只有在没有__str__定义是调用我'
def __str__(self):
return '我是说明对象状态信息的魔法方法,如果不写默认输出对象的地址'
d = D()
print(d)#我是说明对象状态信息的魔法方法,如果不写默认输出对象的地址
注意:
__ str __和 __ repr __魔法都是描述类或对象的信息.定义两个的目的是:
比较操作的魔法方法主要包括以下几种:
__ eq __ ():可以判断亮哥哥对象是否相等
__ ne __ ():判断两个对象是否不相等,这个和__eq__()方法基本一样
__ lt __ ():比较对象的大小,__ lt__ 表示小于
__ gt __ ():比较对象的大小,__ gt__ 表示大于
代码示例:
# 比较操作类魔法方法
class Person():
def __init__(self,uid):
self.uid = uid
def __eq__(self,other):
#截断不相等
return self.uid == other.uid
def __ne__(self,other):
#判断不等于
return self.uid != other.uid
def __lt__(self,other):
return self.uid < other.uid
def __gt__(self,other):
return self.uid > other.uid
p1 = Person(1)
p2 = Person(2)
p3 = Person(2)
print(p1 != p2)
print(p3 == p2)
print(p2 > p1)
print(p1 < p2)
__call__(self, [args…]):当执行对象当做函数使用时,就相当于调用 __ call __。代码示例:
# __call__
class Circle:
def __init__(self,radious):
self.radious = radious
def __call__(self,radious):
self.radious = radious + 1
a = Circle(3)
print(a.radious)#3
a(10)#此时a这个对象相当于一个方法,这是__call__的作用,当执行对象当做函数使用时,就相当于调用 __ call __
print(a.radious)#11
布尔测试
类属性顾名思义就是类所拥有的属性,分为共有属性和私有属性,私有属性通过__属性名称的方法进行定义。对于公有的类属性,可以在类外进行访问,私有属性则不可以,代码如下:
# 类属性,实例属性
class People:
name = 'Me',#共有的类属性
__age = 24#私有属性
def __init__(self):
pass
p = People()
# print(p.name)#('Me',)
# print(People.name)#('Me',)
p.name = 'you'
print(p.name)#you
print(People.name)#('Me',)
# print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性
print(p._People__age)#24
p._People__age = 18
print(p._People__age)#24
print(People._People__age)
# print(People.__age) #错误,不能在类外通过类对象访问私有的类属性
People._People__age = 20
print(People._People__age)#20
print(p._People__age)#28
注意:
"实例变量",但使用需要注意以下几点:
在类中以def关键词定义的都可以称之为实例方法.实例方法最大的特点是:最少要包含一个self参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)。实例方法通常会用类对象直接调用。此外,类的初始方法(__ init __)理论上也属于实例方法,不过它更加特殊.
Python 类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为 cls,类方法是类对象所拥有的方法,需要用修饰器@classmethodcls来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以’cls’作为第一个参数,可以通过实例对象和类对象去访问。类方法的常见应用就是对类属性进行修改.
实例代码如下:
class People:
country = "China"
#类方法,用classmethod装饰器
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls,country):
cls.country = country
p = People()
print(p.getCountry())#可以用过实例对象引用China
print(People.getCountry())#可以通过类对象引用China
p.setCountry('UK')
print(p.getCountry())#UK
print(People.getCountry())#UK
通过修饰器@staticmethod来修饰的的函数.静态方法属于类中的函数.主要作用是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护.例如,我想定义一个关于时间操作的类,其中有一个获取当前时间的函数:
import time
class TimeTest:
def __init__(self,hour,minute,second):
self.hour = hour
self.minute = minute
self.second = second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S",time.localtime())
# 使用类调用静态方法
print(TimeTest.showTime())
# 实例化对象
t = TimeTest(1,10,2)
print(t.showTime())
如上,使用了静态方法,然而静态方法实现中并没使用实例的属性和方法(但可以通过类名调用类属性和类方法)。若要获得当前时间的字符串时,并不一定需要实例化对象。当然,我们也可以在类外面写一个同样的函数来做这些事,但是这样做就打乱了逻辑关系,也会导致以后代码维护困难。
在面向对象的特征多态,有抽象类的概念,即父类只写接口,以保证子类必须实现这些方法.通过装饰器@abc.abstractmethod在父类引入抽象类的概念来硬性限制子类必须有某些方法名.
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass = abc.ABCMeta):
@abc.abstractmethod#该装饰器限制紫烈必须又一个talk方法
def talk(self):#抽象方法,无需实现具体功能
pass
class Cat(Animal):#只要继承了抽象类就必须实现抽象方法
def talk(self):#若子类中没有一个名为talk的方法,则会掏出异常TypeError,无法实例
pass
cat = Cat()
实际上,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚的是“鸭子类型”。
Duck typing:“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。
比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度,如下:
# 鸭子类型
# 定义的类型和运行的类型不一样,就是多态的体现,python崇尚的是鸭子类型,只要有鸭子的方法,就是鸭子
class Cat:
def say(self):
print('I am a Cat')
class Dog:
def say(self):
print("I am a Dog")
class Duck:
def say(self):
print("I am a Duck")
animals_list = [Cat,Dog,Duck]# 这里将三个封装好的类分别作为animals_list的三个元素
for animal in animals_list:
animal().say()# animal()是实例化对象的过程,然后分别调用 Cat, Dog, Duck的say方法
如上述代码所示,如果定义的若干个对象都有同一个方法,(比如下面的say方法),那么无论他们是否继承同一个父类,他们都可以统一通过方法的调用(say方法的调用)实现。
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。 鸭子类型的好处就在于能够避免一些类的重写与继承,无需大量复制相同的代码
在python中模块(module)的概念跟C语言\Java中的包很类似.要使用某个模块下的函数,就必须先使用import关键字导入模块.例如,导入math模块,就可以使用math.sqrt()函数
但在很多时候,我们只需要使用模块中的部分函数,不需要完整的包.可以通过:import 模块名
from 模块名 import 函数名
此时,就可以直接使用函数名了,例如:
from math import sqrt
print(sqrt(4))
from 模块名 import *
import 模块名 as 新的名字
import math as m
print(m.sqrt(4))
def add(x,y):
return x+y
那么在其他文件中就可以通过导入的方式使用,代码如下:
from test import add
print(add(1,2))
但是,在实际开发中,一个模块开发完了,可能会在这个.py文件中添加一些测试模块,用来检验模块的正确性.例如:
def add(x,y):
return x+y
print(add(1,2))
如果此时引入代码,test.py中的print(add(1,2))也会执行.这就给导入带了麻烦.
因此,我们更希望的是字啊python导入模块的时候,只指定脚本中调用的模块函数,而其他函数不应该被执行.为了解决这个问题,python字啊执行一个文件时,会print(__name__),如果在本文件中被执行会打印:_ _ mian _ _,如果该文件被调用了则会打印:__模块名__.
因此,在实际代码开发中,我们经常会看到代码:
if _ _ name _ _ = “_ _ main _ _”
用这种方法来选择性执行被导入的模块代码.例如:
def add(x,y):
return x+y
print(__name__)
if __name__ == '__main__':
print(add(1,2))
实际开发中,一个大型的项目往往需要使用成百上千的 Python 模块,如果将这些模块都堆放在一起,势必不好管理。而且,使用模块可以有效避免变量名或函数名重名引发的冲突,但是如果模块名重复怎么办呢?因此,Python提出了包(Package)的概念。
什么是包呢?简单理解,包就是文件夹,只不过在该文件夹下必须存在一个名为“__init__.py” 的文件,包将有联系的模块组织在一起,即放到同一个文件夹下。
其中,每个包的目录下都必须建立一个 __init__.py 的模块,可以是一个空模块,可以写一些初始化代码,其作用就是告诉 Python 要将该目录当成包来处理。不过,这是 Python 2.x 的规定,而在 Python 3.x 中,init.py 对包来说,并不是必须的。
另外,init.py 不同于其他模块文件,此模块的模块名不是init,而是它所在的包名。例如,在 settings 包中的__init__.py 文件,其模块名就是 settings。包是一个包含多个模块的文件夹,它的本质依然是模块,因此包中也可以包含包。
至于Python 库:相比模块和包,库是一个更大的概念,例如在 Python 标准库中的每个库都有好多个包,而每个包中都有若干个模块
pip 是 python 自带的一个软件,相当于手机里的应用市场,可以用来安装、卸载、搜索 python 的常见模块。
直接输入 pip 后回车,可以查看 pip 命令的所有可用参数。常见的操作如下:
安装模块pip install 被安装模块名
卸载模块pip uninstall 被卸载模块名
搜索模块pip search 模块名
Tips:
看到pip的作用同学们可能会想起conda命令,也可以进行包的下载安装卸载和查询。两者的主要区别是:pip是Python包的通用管理器; conda是一个与语言无关的跨平台环境管理器。pip可以在任何环境中安装python包(只能按照python相关的包,例如C,JAVA其他语言是不行的);conda需要安装在conda环境中装任何包(需要下载Anaconda)。
conda功能其实比pip更多。pip几乎就是个安装包的软件,conda是个环境管理的工具。conda自己可以用来创建环境,pip不能。这意味着你能用conda安装python解释器,pip不行。这一点我觉得是conda很有优势的地方,用conda env可以很轻松地管理很多个版本的python。
其次,conda和pip对于环境依赖的处理不同,总体来讲,conda比pip更加严格,conda会检查当前环境下所有包之间的依赖关系,pip可能对之前安装的包就不管了.这样做的话,conda基本上安上了就能保证工作,pip有时候可能装上了也不work。不过我个人感觉这个影响不大,毕竟主流包的支持都挺不错的,很少遇到broken的情况。这个区别也导致了安装的时候conda算依赖项的时间比pip多。
最重要的是conda作为环境管理工具,可以为不同的任务创建不同的环境,即conda具有环境隔离功能。 对于绝大多数朋友,只是在自己的个人电脑上,其实用pip和conda下载包的区别不大。如果是搞研究或者开发,在一台服务器上多人共享使用,我建议最好是conda。再者,用版本迭代很快的库,我也建议使用conda。