python 类的简单介绍
1 面向过程的程序设计
在说面向对象的程序设计以前,先说下我们之前写的那些面向过程的程序的特点:针对性很强,针对特定的需求所写;流水线式的设计,先实现什么再实现什么,结构非常清晰。但耦合度非常高,牵一发而动全身。
总结面向过程的特点:
- 针对性强,只适用于该需求的程序设计
- 流水线设计,结构清晰。但可扩展性非常差。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
2 什么是面向对象的程序设计
面向对象的程序设计的核心思想:“一切皆对象”。如何理解?我们日常中,遇到的一个人,一本书等,都是一个具体的对象。那我们又是如何认定他是一个人的,一个人有哪些特性?他会说话,他用两只脚走路,他有2只手,用手吃饭。那只要有这些特性的对象,我们就称他为人。而类则是不同对象之间共性的抽象。类里包含了:特征(变量)和技能(函数)。这里所说的“人”,就是一个类。每一个具体的人,张三,李四就是一个具体的对象。对象就是类的具体表现。(有别于其他编程语言,如java类包括属性和方法。)
OOP做出的程序项目优点很多:
- 易维护
- 效率高
- 质量高
- 易扩展
3 类和对象
- 定义一个人类,人有2只腿,用腿走路。
class Person:
legs_amount=2 #共有的属性
def walk(self):
print("person use two legs to walk.")
3.1 类的作用
类的作用:实例化和属性引用(这里类的属性包括变量属性和函数属性,有别于下面的对象属性)
- 属性引用
print(Person.legs_amount) #2
#Person.walk() #TypeError: walk() missing 1 required positional argument: 'self'
print(Person.walk) #<function Person.walk at 0x000001EBEBE7BAE8>
Person.walk(111) #person use two legs to walk.
#调用一个不存在的函数
Person.eat()
#AttributeError: type object 'Person' has no attribute 'eat'
用Person.walk()此时是把walk当普通函数来调用的,所以self作为位置参数必须接收一个值。看Person.eat()这个不存在的方法,错误显示的是“has no attribute”(翻译:不存在的属性),所以python中,类的函数,也称为类的属性。(不同于java)
查看类的属性的方法:Person.__dict__
。
print(Person.__dict__)
结果:
{'__module__': '__main__', 'legs_amount': 2, 'walk': <function Person.walk at 0x000001AE7871BAE8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
- 实例化
如何创建一个对象。将类名加上()来调用(类似于函数),再将它赋值给一个变量。那这个变量就是一个对象。该过程叫实例化对象。
p1=Person()
实例化对象p1以后,就可以直接用p1来调用他自己的属性和方法。
print(p1.legs_amount) #2
print(p1.walk) #<bound method Person.walk of <__main__.Person object at 0x0000023FD3C9A0F0>>
p1.walk() #person use two legs to walk.
此时p1.walk()并不需要额外对self传参,打印p1.walk会发现此时p1.walk是bound method(绑定方法),而上面Person.walk打印的是function(函数)。也就是说类名调用自身的函数,是作为普通函数调用。而对象调用自身的“函数”,就是绑定方法。
对象的属性:本身只有的特征(变量),__init__()
中定义的变量。
查看对象的属性的方法:p1.__dict__
。
print(p1.__dict__)
结果:
{}
为什么对象能引用不在自己名称空间的属性。
绑定方法的核心就是“绑定”,将函数唯一绑定到一个确定的对象。谁调用,就作用于谁。(对象调用绑定方法也叫给对象发消息,告诉对象要干嘛。)
3.2 类中self
那为什么对象调用自身方法,不需要给self传位置参数?在回答这个问题之前,我们先引入数__init__()函数。
class Person:
legs_amount=2 #共有的属性
def __init__(self):
print("this is __init__()")
print(self)
p1=Person() #Person.__init__(p1)
print(p1)
结果:
this is __init__()
<__main__.Person object at 0x000002619383A0F0>
<__main__.Person object at 0x000002619383A0F0>
我们可以发现,在函数实例化的时候,就会自动调用__init__()
函数。(类似于java里的构造函数)
而print(p1)和print(self)显示是用一个Person对象。所以p1=Person()实际执行的是Person.__init__(p1)
。(这里的self可以等同于java中class里的this)
python中的__init__方法只能return None或空,不能return其他值。
3.3 对象与对象之间的交互
一个类实例化的不同对象之间会不会相互影响?
class Person:
legs_amount=2 #共有的属性
def __init__(self,name):
self.name=name
def eat(self):
print("%s eat food"%self.name)
p1=Person("aa")
print("p1.legs_amount:",p1.legs_amount)
p1.eat()
p1.legs_amount=1
print("p1.legs_amount:",p1.legs_amount)
print("----------")
p2=Person("bb")
print("p2.legs_amount:",p2.legs_amount)
p2.eat()
结果
p1.legs_amount:2
aa eat food
p1.legs_amount:1
----------
p2.legs_amount:2
bb eat food
可以发现,每个对象自己的属性(包括共有的属性,即__init__()里定义的变量)无论怎么修改都不会影响其他对象的属性值。p1.legs_amount=1,实际上是在自己的命名空间(__dict__)里面加入了leg_amount变量属性。相当于p1.__dict__["legs_amount"]=1
#print(Person.name) #AttributeError: type object 'Person' has no attribute 'name'
Person.legs_amount=1
print("Person.legs_amount:",Person.legs_amount)
print(",p2.legs_amount:",p2.legs_amount)
结果:
Person.legs_amount:1
p2.legs_amount:1
可以发现,Person并不能修改__init__()里定义的变量,但它可以修改共有的属性legs_amount,此时Person的实例化对象的legs_amount属性都随之发生改变。
3.4 类名称空间与对象/实例名称空间
创建一个类就会创建一个类的命名空间,用来存储类中定义的所有名字,这些名字统称为类的属性。
类里面有两种属性:数据属性和函数属性
其中类的数据属性是共享给所有对象的。
p1=Person("aa")
print(id(p1.legs_amount)) #1358668928
print(id(Person.legs_amount)) #1358668928
而类的函数属性是绑定到所有对象的。
print(id(p1.eat)) #2271362786824
print(id(Person.eat)) #2271394904944
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常。 **
注意:创建类会创建类的命名空间,但不会创建作用域,所以在类里面调用类里定义的变量或函数,就必须用obj.name或类名.name**
3.5 增加删除类和对象的属性
#添加属性
Person.hands_amount=2
print("Person.hands_amount:",Person.hands_amount)
print("p1.hands_amount:",p1.hands_amount)
p1.age=20
print("p1.age:",p1.age)
#print(p2.age) #AttributeError: 'Person' object has no attribute 'age'
结果:
Person.hands_amount: 2
p1.hands_amount: 2
p1.age: 20
添加和修改属性的方法:类名或对象.属性=值。Person添加的属性,是大家共有的。对象添加的是对象私有的。删除属性:del 类名或对象.属性
#删除属性
Person.hands_amount=2
print("Person.hands_amount:",Person.hands_amount)
del Person.hands_amount
print("Person.hands_amount:",Person.hands_amount)
结果:
Person.hands_amount: 2
AttributeError: type object 'Person' has no attribute 'hands_amount'
补充说明
在python里面,类或对象.变量名的访问方式,是先在类或变量的命名空间中寻找该变量名
3.6 总结
- 类名加()赋值给一个变量,就叫实例化。
- 实例化的时候,会优先调用
\_\_init\_\_()
方法。其中self就是对象本身。实例化的过程就是:类名.\_\_init\_\_(对象名)
。 - 类调用自己的函数,就是普通的调用。对象调用自身的函数,此时的函数叫绑定方法。
- 可以直接在类的外部给类或该类的实例添加修改删除属性。
- 类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响。
4 类的特殊属性
通常情况,类中以“__变量名”这种形式的,是私有变量。而以“__变量名__”的一类变量,有其特殊的意义。下面我们来介绍一下class里面这类特殊变量的具体用途。
class Student:
"student"
school_name="society_school"
def __init__(self,name,score):
self.name=name
self.score=score
def print_score(self):
print("%s 's score is %s"%(self.name,self.score))
s=Student("alen",98)
s.print_score()
结果:
alen 's score is 98
{'name': 'alen', 'score': 98}
4.1 查看类属性
4.1.1 dir()
打印类下的所有属性和方法,或打印对象的所有属性和方法(包括类的所有属性和它自己特有的属性)。返回的是一个名字的列表。
print(dir(s))
print(dir(Student))
结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'print_score', 'school_name', 'score']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'print_score', 'school_name']
4.1.2 __dict__()
以键值对的方式打印,对象私有的属性和属性值,或类本身的属性和属性值。返回的是
print(s.__dict__)
print(Student.__dict__)
结果:
{'name': 'alen', 'score': 98}
{'__module__': '__main__', 'school_name': 'society_school', '__init__': <function Student.__init__ at 0x0000018EE36FBAE8>, 'print_score': <function Student.print_score at 0x0000018EE36FBB70>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
4.2 特殊的类属性
__name__
类名.\_\_name\_\_
以字符串的形式返回类的名字
print(Student.__name__)
print(s.__name__)
结果:
Student
AttributeError: 'Student' object has no attribute '__name__'
__doc__
类名(或对象名).\_\_doc\_\_
类的文档字符串
print(s.__doc__)
print(Student.__doc__)
结果:
student
student
__base__
类名.__base__# 类的第一个父类
__bases__
类名.__bases__# 类所有父类构成的元组
__dict__
类名.__dict__# 类的字典属性
__module__
类定义所在的模块
print(s.__module__)
print(Student.__module__)
结果:
__main__
__main__
__class__
__class__和type()功能一样
print(s.__class__)
print(Student.__class__)
print(type(s))
print(type(Student))
结果:
<class '__main__.Student'>
<class 'type'>
<class '__main__.Student'>
<class 'type'>