前面梳理回顾了python的入门基础知识,一共有三篇从python的下载安装运行环境配置,到如何定义变量、基本数据类型、条件语句、循环语句、python中list容器 dict容器定义及数组数据的用法、函数的基础知识。请参考:
Python 3 入门基础知识【1】数据类型 安装下载 推荐-CSDN博客
Python 3 入门基础知识 之数据容器及用法【2】 推荐-CSDN博客
Python 3 入门基础知识【3】递归函数以及参数部分-CSDN博客
这里是梳理了下python面向对象编程以及类的的内容。
目录
3.python的数学运算__add__、__sub__、__mul__、__truediv__
一、python面向对象编程
1.class类与实例化
1.1 类的定义
在Python中,通过class关键字定义一个类,比如我们需要定义一个人的类。按照 Python 的编程习惯,类名以大写字母开头。
# 第一种方式
class Person: pass
# 第二种方式
class Person(): pass
#第三种方式
class Person(object): pass
类属性
类和实例对象是有区别的,类是抽象,是模板,而实例则是根据类创建的对象,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。定义类属性可以直接在 class 中定义,比如在前面的Animal类中,加入地域的类属性:
class Animal(object):
localtion = 'Asia'
def __init__(self, name, age):
self.name = name
self.age = age
localtion就是属于Animal这个类的类属性,此后,通过Animal()实例化的所有对象,都可以访问到localtion,并且得到唯一的结果
dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
print(dog.localtion) # ==> Asia
print(cat.localtion) # ==> Asia
# 类属性,也可以通过类名直接访问
print(Animal.localtion) # ==> Asia
类属性也是可以动态添加和修改的,需要注意的是,类属性只有一份,改变了,所有实例可以访问到的类属性都会变更:
Animal.localtion = 'Africa'
print(cat.localtion) # ==>Africa
print(dog.localtion) # ==>Africa
1.2 实例化及属性
类的实例化
# 定义类
class Person(object): pass
# 类的实例化
xiaohong = Person()
给实例增加属性
xiaohong.age = 18
print(xiaohong.age) #18
xiaohong.age = xiaohong.age + 1 #可以像变量一样运算
print(xiaohong.age)
初始化具有属性的实例
这里 __init__() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),实例类除了第一个self,其他参数都要有,访问属性和上面一样,如果访问不存在的属性会报错。
1.3 类属性和实例属性的优先级
如果类属性和实例属性名字相同时,我们在前面类定义的基础上,在实例属性中,也初始化一个localtion的属性。
class Animal(object):
localtion = 'Asia'
def __init__(self, name, age, localtion):
self.name = name
self.age = age
self.localtion = localtion
我们实例化并打印看结果如何
dog = Animal('wangwang', 1, 'GuangDong')
cat = Animal('mimi', 3, 'ChongQing')
print(dog.localtion) # ==> GuangDong
print(cat.localtion) # ==> ChongQing
print(Animal.localtion) # ==> Asia
可见,在类属性和实例属性同时存在的情况下,实例属性的优先级是要高于类属性的,在操作实例的时候,优先是操作实例的属性
当实例没有和类同名的时候,通过实例对象,依然可以访问到类属性,那通过实例,可不可以修改类属性呢?
class Animal(object):
localtion = 'Asia'
def __init__(self, name, age):
self.name = name
self.age = age
cat = Animal('mimi', 3)
print(cat.localtion) # ==> Asia
cat.localtion = 'Africa'
print(Animal.localtion) # ==> Asia
可见通过实例是无法修改类的属性的,事实上,通过实例方法修改类属性,只是给实例绑定了一个对应的实例属性:
# 新增的实例属性
print(cat.localtion) # ==> Africa
注意,尽量不要通过实例来修改类属性,否则很容易引发意想不到的错误。
1.4 私有属性访问限制
并不是所有的属性都可以被外部访问的,这种不能被外部访问的属性称为私有属性。私有属性是以双下划线'__'开头的属性。
# 类私有属性
class Animal(object):
__localtion = 'Asia'
# 实例私有属性
class Animal(object):
def __init__(self, name, age, localtion):
self.name = name
self.age = age
self.__localtion = localtion
print(Animal.__localtion)
print(dog.__localtion)
#提示错误
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute '__localtion'
在外部访问私有属性将会抛出异常,提示没有这个属性。
虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。私有属性是为了保护类或实例属性不被外部污染而设计的。
1.5 定义实例方法
class Person(object):
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
name是实例的私有属性,从外部是无法访问的,而get_name(self) 就是一个实例方法,在实例方法里面是可以操作私有属性的,注意,它的第一个参数是self。
p = Person('Alice')
print(p.get_name()) # ==> Alice
通过定义实例方法来操作私有属性的这种方法是推荐的,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。实例方法并不仅仅是为私有属性服务的,我们可以把和类的实例有关的操作都抽象成实例方法,比如:打印实例的详细信息等等。
1.6 Python定义类方法
如果需要需要操作类的私有属性,则应该定义类的方法。
默认的,在class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。类方法需要使用@classmethod来标记为类方法
class Animal(object):
__localtion = 'Asia'
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def set_localtion(cls, localtion):
cls.__localtion = localtion
@classmethod
def get_localtion(cls):
return cls.__localtion
print(Animal.get_localtion()) # ==> Asia
Animal.set_localtion('Afica')
print(Animal.get_localtion()) # ==> Africa
- 类方法需要使用@classmethod来标记为类方法,否则定义的还是实例方法
- 类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.__localtion 实际上相当于Animal.__localtion
二、类的继承
1.python的继承类
类是抽象的,当一个类中定义了一些相同的属性,而另一个类也需要定义相同的属性,那么这个时候我们可以直接集成其他类,只需要新增自己需要的属性即可。比如老师和学生都属于人类,这个时候就定义一个Person类:
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
现在我们定义学生类,比如也有name和gender属性,另外多了一个学分,我们就可以继承Person类。
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
student = Student('Alice', 'girl', 100)
print(student.name) # ==> Alice
print(student.gender) # ==> girl
print(student.score) # ==> 100
- class Student()定义的时候,需要在括号内写明继承的类Person
- 在__init__()方法,需要调用super(Student, self).__init__(name, gender),来初始化从父类继承过来的属性
2.函数isinstance()
前面我们回顾了python的基本数据类型,整数、浮点型、布尔型等,那我们如何知道一个变量是哪种数据类型呢?这个时候就要用到函数isinstance()方法了,这个方法可以判断一个变量的类型。
上面的person和student类分别实例化
p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
>>> isinstance(p, Person)
True # p是Person类型
>>> isinstance(p, Student)
False # p不是Student类型
>>> isinstance(s, Person)
True # s是Person类型
>>> isinstance(s, Student)
True # s是Student类型
说明在继承链上,一个父类的实例不能是子类类型,因为子类比父类多了一些属性和方法。s 也是Person类型,因为Student继承自Person,虽然它比Person多了一些属性和方法,但是,把 s 看成Person的实例也是可以的。在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型。
isinstance也可以用于Python自有数据类型的判断。
s = 'this is a string.'
n = 10
isinstance(s, int) # ==> False
isinstance(n, str) # ==> False
3.多肽
在下面这个例子中,student和teacher类分别继承自 Person类,子类和父类分别有一个who方法,我们实例化后分别把函数内打印出来。我们可以看到打印结果,这种就被称为多态。
从定义上来讲,Student和Teacher都拥有来自父类Person继承的who()方法,以及自己定义的who()方法。但是在实际调用的时候,会首先查找自身的定义,如果自身有定义,则优先使用自己定义的函数;如果没有定义,则顺着继承链向上找:
class Boss(Person):
def __init__(self, name, gender,company):
super(Boss, self).__init__(name, gender)
self.company = company
b = Boss('Bob', 'Male', 'Alibaba')
# 在Boss的定义类,没有定义who方法,所以会顺着继承链向上找到父类的who方法并且调用。
b.who() # ==> I am a Person, my name is Bob
多重继承
除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。多重继承和单继承没有特别大的差异,只是在括号内加入多个需要继承的类的名字即可。
在多重继承里,父类虽然被继承了两次,但是__init__()的方法只调用一次。多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。
Python的网络服务器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服务器运行模式有 多进程ForkingMixin 和 多线程ThreadingMixin两种。如果没有多重继承,要实现下述所有可能的组合需要 4x2=8 个子类。
# 要创建多进程模式的 TCPServer:
class MyTCPServer(TCPServer, ForkingMixin)
pass
# 要创建多线程模式的 UDPServer:
class MyUDPServer(UDPServer, ThreadingMixin):
pass
4.获取对象信息
4.1 type()方法
我们通过isinstance()方法,可以判断一个对象是否是某个类型,通过type()函数,可以获得变量的类型。
n = 1
s = 'this is a string'
type(n) # ==> <class 'int'> #获得变量类型 int
type(s) # ==> <class 'str'>
4.2 dir()方法
通过dir()方法,可以获取变量的所有属性。在dir列出的属性中,有很多是以下划线开头和结尾的,这些都是特殊的方法,称为内建方法
>>> n = 1
>>> dir(n)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', ...]
>>> s = 'this is a string'
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', ...]
4.3 getattr() 和 setattr( )
dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() 和 setattr( )函数了。
>>> getattr(p, 'name') # 获取name属性
'Alice'
>>> setattr(p, 'name', 'Adam') # 设置新的name属性
>>> s.name
'Adam'
>>> getattr(s, 'age') # 获取age属性,但是属性不存在,报错:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'
>>> getattr(s, 'age', 20) # 获取age属性,如果属性不存在,就返回默认值20:
20
三、特殊方法
python中的特殊方法有两个下划线开始和两个下划线结束。
1.__str__ 和 __repr__方法
对于Python的内建对象,比如int、dict、list等,通过str()方法,可以把这些对象转换为字符串对象输出。 通过str()打印的数据是对象的内建方法__str__返回的。
num = 12
str(num) # ==> '12'
d = {1: 1, 2: 2}
str(d) # ==> '{1: 1, 2: 2}'
l = [1,2,3,4,5]
str(l) # ==> '[1, 2, 3, 4, 5]'
对于自定义对象,通过str()方法,同样可以得到对象所对应的字符串结果,只不过结果会有些难理解
如果我们的类也想把容易理解的字符串输出的话,那么我们也需要实现类的__str__()方法。
这是因为 Python 定义了__str()__和__repr__()两种方法,__str()__用于显示给用户,而__repr__()用于显示给开发人员,当使用str()时,实际调用的是__str__()方法,而直接输入变量,调用的是__repr__()方法。
2.__len__方法
对于列表List或者元组Tuple,通过内建方法len(),可以得出列表或者元组中元素的个数。如果一个类表现得像一个list,想使用len()函数来获取元素个数时,则需要实现__len__()方法。
class Class:
def __init__(self, students):
self.students = students
def __len__(self):
return len(self.students)
students = ['Alice', 'Bob', 'Candy']
class_ = Class(students)
len(class_) # ==> 3
3.python的数学运算__add__
、__sub__
、__mul__
、__truediv__
事实上,Python很多的操作都是通过内建函数来实现的,因此,只要我们的自定义类实现了相关的内建函数,我们的类对象,也可以做到加减乘除。
class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
def __add__(self, r):
return Rational(self.p * r.q + self.q * r.p, self.q * r.q)
def __str__(self):
return '{}/{}'.format(self.p, self.q)
r1 = Rational(1, 2)
r2 = Rational(2, 3)
print(r1 + r2)
7/6
Python的除法可以分为地板除和普通除法,地板除的特殊方法是__floordiv__,普通除法是__truediv__。地板除法和普通除法不一样,地板除法的结果只会向下取整数。 普通除法使用/表示,而地板除使用//表示
4.__slots__方法
由于Python是动态语言,任何实例在运行期都可以动态地添加属性,如果要限制添加的属性,就可以利用Python的一个特殊的__slots__来实现。
class Student(object):
__slots__ = ('name', 'gender', 'score')
def __init__(self, name, gender, score):
self.name = name
self.gender = gender
self.score = score
__slots__的目的是限制当前类所能拥有的属性,避免因为外部属性的操作导致类属性越来越难以管理。
5.__call__方法
如果把一个类实例也变成一个可调用对象,可以实现一个特殊的方法__call__()。
我们把Person类变成一个可调用对象:
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __call__(self, friend):
print('My name is {}...'.format(self.name))
print('My friend is {}...'.format(friend))
p = Person('Bob', 'Male')
p('Alice') # ==> 用函数的方式调用Person类的实例p
My name is Bob...
My friend is Alice...
到这里我们把
- python的面向对象编程,包括class类与实例化、类和实例化的属性以及优先级对比、私有属性的访问限制和类与实例化的方法;
- 类的继承、多态、isinstance()方法以及其他获取对象的信息的方法;
- python中的一些特殊方法:__str__、__repr__、__len__、__add__等方法。
这些知识进行了梳理,欢迎大家评论区探讨和交流,谢谢。
后续会继续总结python的模块、读写文件、网络编程、函数式编程。