共计 10068 个字符,预计需要花费 26 分钟才能阅读完成。
引言
这篇文章介绍 Python 如何将函数与数据整合成类,并通过一个对象名称来拜访它们。
文章目录
0×1. 什么是 python 中的对象
0×2.python 中如何创立类
0×3. 类公有属性拜访限度
0×4. 继承与多态
0×1. 什么是 python 中的对象
Python 中每条数据都是对象,每个对象都由三局部组成:标识,数据,类型;
标识是对象的名称,也贮存了该对象的内存地址(对象援用),类型规定了这个对象所能贮存的值的类型,内存中的某块区域保留了对象的数据值;
在之前的文章中,咱们定义的字符串,列表,字典等,都是对象,每个对象都有其内置的一些办法,例如对字符串对象应用 len()办法能够求出字符串的长度,咱们能够在 IDLE 中应用 dir()办法来查看某个对象能够应用的办法,如下:
>>> s="www.qingsword.com"
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
#下面中括号中的所有输入,以逗号分隔,每一个都是字符串对象能够应用的办法,其中带双下划线的为 "外部办法",不带下划线的为 "内部办法" 也叫 "接口函数","外部办法" 通常对用户是不可见的,但这并不意味着外部办法就是不可调用的,实际上咱们对某个字符串对象应用 len()函数的时候,就是调用了其外部的__len__办法,通过上面的实例能够看到,两者的输入是雷同的
>>> print(len(s))
17
>>> print(s.__len__())
17
在定义一个字符串对象的时候,python 在其外部初始化了一个 str 类,这个类中蕴含了 python 提供的对字符串解决的内置办法,等大家看完本文第 2 局部,本人手动创立了一个类之后,就天然会明确下面这些外部办法和接口函数的工作原理了。
0×2.python 中如何创立类
python 中的类,实际上是实现某个工作的对象和函数的汇合,当一个类被调用时,它创立了绑定到一个名称的对象,请看上面的实例,这是一个经典的电冰箱实例,咱们假如要创立一个类,用于将食物放入冰箱,查问冰箱中有哪些食物,而后提供取出食物的一系列办法:
#!/usr/bin/env python3
#coding=utf-8
########
class Fridge:
"""电冰箱类实例"""
#--------
def __init__(self,items={}):
"""类初始化"""
if type(items)!=type({}):
raise TypeError("传入参数谬误:%s"%items)
self.items=items
########
#python 应用 class 关键字来定义一个类,本例定义了一个 Fridge 类,在这个类中有一个外部办法__init__,每个类都能够蕴含这个办法,也能够不蕴含,这个办法的作用是,当应用名称初始化一个 Fridge 对象时,主动执行__init__函数中的代码,相当于类的初始化操作
#留神 def __init__(self,items={})局部中的 self 参数,这是 python 独有的语法,self 总是代表类对象自身,这段初始化代码初始化了一个空字典对象,如果不好了解 self,能够尝试着将代码中的 self 省去,而后察看这段函数,实际上__init__就接管一个参数,这个参数必须是一个字典对象,如果省去这个参数,那么默认初始化一个空自字典,并且在 Fridge 外部应用 items 对象保留这个字典的数据,但理论状况下 self 参数不能省略
#当初咱们曾经有了一个 Fridge 类,能够通过上面的办法创立这个类的对象实例,上面的语法没有给 Fridge 传递参数,所以将初始化一个空的字典,如果想要传递参数,能够这么写 p =Fridge({"apple":1,"orange":3}),如果这样,那么 Fridge 中的 items 对象就将保留 {"apple":1,"orange":3} 这个字典
p=Fridge()
#当初有一个新的对象 p,他是一个残缺的 Fridge 类对象,在下面的创立过程中,Fridge 类执行了__init__办法,这将创立一个空的字典对象,能够通过 "类实例名称. 类对象名称" 来拜访到这些数据
print(p.items)
#输入
{}
当初,能够给 Fridge 类增加一系列办法来提供必要的性能了,在 Fridge 类中补充上面的内容,用于增加查问或删除食物:
#!/usr/bin/env python3
#coding=utf-8
########
class Fridge:
"""电冰箱类实例"""
#--------
def __init__(self,items={}):
"""类初始化"""
if type(items)!=type({}):
raise TypeError("传入参数谬误:%s"%items)
self.items=items
#--------
def __add_multi(self,food_name,quantity):
"""外部办法,向 items 字典增加键值,如果 food_name 不在 items 字典的键列表中,阐明这是一个新增加的食物,将数量初始化为 0,而后加上传递给这个函数的 quantity 的值(可能是一个或者多个)"""
if (not food_name in self.items.keys()):
self. items[food_name]=0
self.items[food_name]+=quantity
#--------
def add_one(self,food_name):
"""向 items 字典增加单个食物,这就是一个接口函数,通过调用外部办法__add_multi 实现增加食物到冰箱的工作"""
if type(food_name)!=type(""):
raise TypeError("食物名称类型谬误:%s,正确的数据类型:%s"\
%(type(food_name),type("")))
else:
self.__add_multi(food_name,1)
return True
#--------
def add_many(self,food_dict):
"""像 items 字典增加多个食物,这个接口函数接管一个字典参数 food_dict,应用 for 循环遍历这个传递过去的字典中的键名,而后将名称和数量传递给__add_multi 函数"""
if type(food_dict)!=type({}):
raise TypeError("食物名称和数量必须应用字典,谬误的数据输出:%s"%food_dict)
else:
for food_name in food_dict.keys():
self.__add_multi(food_name,food_dict[food_name])
#--------
def has(self,food_name,quantity=1):
"""查看 items 中是是否还有某个食物,如果只向这个接口函数传递一个食物名称,那么 quantity 默认为 1,他将调用上面的 has_various 函数,将食物名称和食物数量传递过来,用于判断冰箱中还有没有这么多的食物"""
return self.has_various({food_name:quantity})
#--------
def has_various(self,foods):
"""查看食物是否低于输出值,这个接口函数承受一个字典类型的传入参数,通过遍历这个传入的字典中的键名,判断 Fridge 的 items 字典中对应键的值是否小于传递进来的这个列表中的每个食物的值,如果小于就返回 False,代表冰箱中没有那么多食物了,如果传入的食物名称不存在,会产生一个 KeyError 异样,同样返回 False"""
try:
for food in foods.keys():
if self.items[food]<foods[food]:
return False
return True
except KeyError:
return False
#--------
def __get_multi(self,food_name,quantity):
"""取出食物的外部办法,这个办法承受两个值,一个为食物名称,一个为食物数量,首先 if 判断冰箱中是否存在这种食物,如果有将其从类实例的 items 字典中减去对应数量,减去数量后再判断这种食物库存是否为 0,如果为 0,将其从 items 字典中删除,如果冰箱中没有这种食物或者库存有余(这些都在 has_various 办法中判断),跳转到 else 语句,判断是库存有余还是基本没有这种食物"""
if self.has(food_name,quantity):
self.items[food_name]-=quantity
print("胜利取出 %s, 数量 %s"%(food_name,quantity))
if self.items[food_name]==0:
self.items.pop(food_name)
else:
if not food_name in self.items.keys():
print("冰箱中并没有这种食物:%s"%food_name)
else:
print("食物'%s'库存有余,现有数量:%s"%(food_name,self.items[food_name]))
#--------
def get_one(self,food_name):
"""取出某个食物,调用外部办法__get_multi 取出单个食物"""
if type(food_name)!=type(""):
raise TypeError("食物名称类型谬误:%s,正确的数据类型:%s"\
%(type(food_name),type("")))
else:
self.__get_multi(food_name, 1)
return True
#--------
def get_many(self,food_dect):
"""取出多个食物,这个函数接管一个字典参数,for 循环遍历这个传入的字典中的键列表,调用__get_multi 取出这些食物和对应的数量"""
if type(food_dect)!=type({}):
raise TypeError("食物名称和数量必须应用字典,谬误的数据输出:%s"%food_dict)
for food in food_dect.keys():
self.__get_multi(food,food_dect[food])
########
#初始化 Fridge 类
p=Fridge()
#增加食物
p.add_one("orange")
p.add_many({"apple":2,"banana":5})
print(p.items)
#输入
{'apple': 2, 'orange': 1, 'banana': 5}
#判断食物是否存在
print(p.has("orange")) #True
print(p.has("apple",3)) #False
#取出食物
if p.has("orange"):
p.get_one("orange")
p.get_many({"banana":4,"apple":4})
p.get_one("www.qingsword.com")
print(p.items)
#输入
胜利取出 orange, 数量 1
胜利取出 banana, 数量 4
食物 'apple' 库存有余,现有数量:2
冰箱中并没有这种食物:www.qingsword.com
{'apple': 2, 'banana': 1}
0×3. 类公有属性拜访限度
在下面的电冰箱实例中,咱们曾经创立过了公有办法,python 中所谓的公有办法和属性,就是在这些办法或属性名前增加两个 ” 下划线 ”(例如__items),这样的属性或办法是不能用失常的办法拜访到的,但不包含前后双下划线的那些(例如__len__),上面用一个实例来演示这些公有属性和办法的个性:
#!/usr/bin/env python
#coding=utf-8
########
class Kitty(object):
"""公有属性__name 和__age 的类"""
#--------
def __init__(self,name="hello",age=1):
"""初始化"""
self.__name=name
self.__age=age
#公有属性能够通过接口函数来调用
#--------
def get_age(self):
"""返回年龄"""
return self.__age
#--------
def set_age(self,age):
"""设置年龄"""
self.__age=age
#--------
def get_name(self):
"""返回名字"""
return self.__name
#--------
def set_name(self,name):
"""设置名字"""
self.__name=name
#前后双下划线的办法,不是公有办法,内部能够间接拜访,只有前置双下划线的属性或办法才是公有的,自能通过外部接口函数调用
#--------
def __get_name_len__(self):
return len(self.__name)
#实例化
kt=Kitty()
#通过接口函数设置年龄和姓名
kt.set_age(2)
kt.set_name("www.qingsword.com")
#通过接口函数打印姓名和年龄
print(kt.get_name())
print(kt.get_age())
#打印出名字长度
print(kt.__get_name_len__())
#程序输入
www.qingsword.com
2
17
#从这个实例能够看出,前后带双下划线的办法或属性,都能间接拜访到,而如果咱们输出上面这 3 句,发现并没有报错,不是说公有属性不能间接拜访吗,为什么能够间接 kt.__name 设置公有属性的值呢?这是因为,实际上咱们应用实例 kt.__name 这种语法的时候,是对 kt 这个类实例,创立了一个新的属性__name,这个属性和 Kitty 外部的公有属性并不是一个属性,外部的__name 属性曾经被 python 解释器主动改名为_Kitty__name 了
kt.__name="qingsword.com"
print(kt.__name)
print(kt._Kitty__name)
#从上面的程序输入能够看到,两者保留的数据并不相同
qingsword.com
www.qingsword.com
#这并不意味着内部就真的无奈调用这些公有属性或办法,只是 python 解释器主动对这些公有数据增加了类名前缀,能够给公有属性增加带但下划线的类名前缀来间接拜访他们,但并不举荐这么做
kt._Kitty__name="www.qingsword.com"
#可能大家会感觉很奇怪,为什么要增加这些公有的属性或办法,间接提供拜访不好吗?实际上,能够通过创立接口函数,对公有数据进行过滤解决,例如
#--------
def set_age(self,age):
"""设置年龄"""
if 0<=age<=150:
self.__age=age
else:
print("年龄不迷信")
0×4. 继承与多态
咱们在创立类的时候,默认状况下都是继承了 python 的基类 object,除此之外,咱们还能让新建的类继承现有的类,继承后子类将领有被继承的父类所有的属性和办法,上面用一个实例来演示这一个性:
#!/usr/bin/env python
#coding=utf-8
########
class Parent(object):
"""父类,继承了 object 类,如果一个类是继承基类 object,能够省略 (object) 不写,但写上看起来更加的标准"""
#--------
def print_website(self):
"""打印网址"""
print("www.qingsword.com")
#--------
def print_author(self):
"""打印作者名称"""
print("QingSword")
########
class Sub_A(Parent):
"""继承 Parent 的子类,将父类写在子类前面的括号中,示意继承其所有的办法和属性,Sub_A 重写了父类的 print_website 办法"""
#--------
def print_website(self):
"""打印网址"""
print("qingsword.com")
########
class Sub_B(Parent):
"""继承 Parent 的子类,仅仅是一个空类,但却能够应用父类所有属性或办法(留神,对空类最好增加一个这样的正文,否则在某些编辑器下可能会呈现缩进 IndentationError 谬误)"""
########
class Sub_C(object):
"""继承 object 的子类"""
#--------
def print_author(self):
print("qingsword")
#--------
def print_info(x):
"""打印作者信息"""
x.print_author()
#首先实例化四个类
p=Parent()
a=Sub_A()
b=Sub_B()
c=Sub_C()
#拜访父类的两个办法
p.print_website()
p.print_author()
#程序输入
www.qingsword.com
QingSword
#拜访子类 A 的两个办法,如果一个子类中有与父类同名的属性或办法,那么它将笼罩父类的属性或办法,A 类中重写了父类的 print_website(),所以会优先被调用,尽管 A 类并没有 print_author()办法,然而它将从父类 Parent 中继承这个办法,除非重写这个办法,否则就将间接调用父类的办法打印出作者信息
a.print_website()
a.print_author()
#程序输入
qingsword.com
QingSword
#B 类中什么都没写,但他继承了父类 Parent
b.print_author()
b.print_website()
#程序输入
www.qingsword.com
QingSword
#再来看看这些对象他们的类型都是什么
#Parent 类是继承 object 的,所以它能够是 object 类型,也能够是 Parent 类型,但不能是其子类型
print(isinstance(p,Parent))
print(isinstance(p,object))
print(isinstance(p,Sub_A))
#程序输入
TRUE
TRUE
FALSE
#所有的子类型,能够是其父类型或 Python 基类 object,也能够是其本身,所以上面的输入都是 True
print(isinstance(a,Parent))
print(isinstance(a,Sub_A))
print(isinstance(b,Parent))
print(isinstance(b,Sub_B))
#像 print_info()办法传递四个类,四个类能够是不同类型,甚至能够像 print_info()传递其余办法名称,这是高级动静语言的一个最弱小的中央——多态:print_info()办法接管一个变量,它不论接管的变量是什么类型,只关怀这个变量能不能拜访 print_author()办法,这就是为什么 C 类尽管是继承了 object,但它也有 print_author()办法,所以程序不会报错
print_info(p)
print_info(a)
print_info(b)
print_info(c)
#程序输入
QingSword
QingSword
QingSword
qingsword