类和对象(一)
Python
中一切皆对象。
1. 基本概念
- 类(class):类就是对某一类事物的一个抽象,用来描述具有相同属性和方法的类型集合。
- 对象(object):对象就是类的一个实例。
- 实例化:由类到对象的一个过程。
- 属性:从属于对象或者类的变量叫做属性。(包括静态属性和动态属性)
- 方法(method):对象可以通过类中的函数来实现相关功能,这个函数叫做类的方法。(方法分为普通方法、类方法和静态方法)
2. 类的定义和实例化
语法格式如下:
class 类名:
执行语句…
零到多个类变量…
零到多个方法…
类中最重要的就是变量和方法,例如我们可以定义如下类:
class Person:
name = None
gender = None
age = None
def set_person_info(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def eat_meal(self):
print("{} is eating...".format(self.name))
def speak(self):
print("{} is speaking...".format(self.name))
上面定义了一个Person
类,它有属性name、gender、age
三个属性以及两个方法。
下面就可以对该类进行实例化了。
p = Person() # 这样就实例化了一个Person
p.set_person_info("Meng", "male", 18) # 调用类中的方法
3. 类中的属性和方法
3.1 属性
3.1.1 类属性
在类命名空间中定义的属性就是类属性,可以直接使用类来进行读取和修改的属性。
class Person:
gender = "male" # 定义的类属性
print(Person.gender) # 直接通过类来访问类属性可以正常访问
Person.gender = "female" # 直接通过类名来修改类属性,可以修改成功
上面示例中定义的gender
属性为公有类属性,我们还可以定义私有类属性,在变量名前加上双下划线,这样就定义成了私有属性,当然变量的结尾不能也为双下划线,如果前后均为双下划线将仍为公有属性,如下:
class Person:
__gender = "male" # 前面加两个下划线,我们就无法直接在类外进行访问
print(Person.__gender) # 这样不能成功访问
如果非要访问私有类属性,也不是不可以,可以通过Person._Person__gender
来进行访问。为了防止类中的私有属性被子类或实例化对象修改,Python
内部对其进行了命名修饰。当然如果要设置私有方法,也同样的是在方法前面加上双下划线。
3.1.2 实例属性
声明在方法中,通过self声明的属性,是实例对象所特有的属性。无法通过类来调用实例对象的实例属性。
class Person:
def __init__(self):
self.name = "Meng" # 实例属性,python中,在调用该函数时,self会自动绑定到实例对象
self.age = 18
p = Person() # 此时会调用__init__()方法
print(p.name) # 输出实例属性
p.gender = "male" # 在类外定义实例属性
del p.name # 删除实例属性
print(p.name) # 报错AttributeError: 'Person' object has no attribute 'name'
3.2 方法
实例方法
必须要创建实例才能调用,方法中的第一个参数必须是实例对象,该参数名一般为self
,通过它来传递实例属性和方法(也可以传递类的属性和方法)。
类方法
使用装饰器@classmethod
。方法中第一个参数必须为当前类对象,该参数名一般约定为cls
,通过它来传递类的属性和方法(不能传实例的属性和方法)。实例对象和类对象都可以调用它。
静态方法
使用装饰器@staticmethod
。参数随意,与正常函数相同,但是方法体中不能使用类或实例的任何属性和方法。实例对象和类对象都可以调用它。
import time
class Person:
ability = ["eat", "drink", "play", "hahaha"]
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def eat(self): # 实例方法
print("{} is eating...".format(self.name))
@classmethod
def list_ability(cls): # 类方法
print("Person can", ", ".join(cls.ability), "and so on.")
@staticmethod
def say_time(): # 静态方法
print("The time is", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
p = Person("Meng", 18, "male")
p.eat() # 实例对象调用实例方法
p.list_ability() # 实例对象调用类方法
Person.list_ability() # 类对象调用类方法
p.say_time() # 实例对象调用静态方法
Person.say_time() # 类对象调用静态方法
3.3 类的特殊方法
3.3.1 常用特殊方法
-
__new__()方法:
该方法是在新式类中新出现的方法,它在构造方法构造实例之前调用,也就是在调用__init__()
方法之前调用,__new__()
可以决定是否调用__init__()
方法。该方法始终都是静态类方法,即使没有加上静态方法装饰器。class Singleton(object): def __new__(cls): # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象 if not hasattr(cls, 'instance'): cls.instance = super(Singleton, cls).__new__(cls) return cls.instance obj1 = Singleton() obj2 = Singleton() obj1.name = "Meng" print(obj2.name) print(id(obj1)) print(id(obj2)) #=====output==== Meng 1480192366856 1480192366856
单例模式:是一种常用的软件设计模式,在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
-
__init__()方法:
构造方法,在创建实例时会自动执行,一般用于初始化对象中的各种不参数。 -
__call__()方法:
实例化对象后,在对象后面加上括号会执行该方法。class Person: def __call__(self, *args, **kwargs): print("call Me") person = Person() person()# 这里将会调用__call__()方法
-
__del__()方法:
析构方法,与__init__()
方法相对应,用于销毁对象,在使用del
删除对象或者代码执行结束时,系统都会自动调用对象中的__del__()
方法。 -
__str__()方法:
可以直接调用该方法将一个对象转换成一个字符串。如果类中重写了该方法,在用print
打印实例或者调用str()
方法时,会输出该方法中返回的内容,而不是调用object.__str__()
方法。在直接打印一个实例对象的时候,会先调用实例对象中重写的__str__()
方法,如果没有则调用实例对象中重写的__repr__()
方法,如果这两个方法都没有重写,则调用object.__str__()
方法(该方法返回一个内存地址)。 -
__repr__()方法:
当程序需要将任何对象与字符串进行连接时,都可先调用__repr__()
方法将对象转换成字符串,然后将两个字符串连接在一起。或者直接使用repr()
函数来将对象转换为字符串。 -
__len__()方法:
当调用len(object)
时会触发,一般是返回一个长度值。 -
__doc__()方法:
打印当前类的描述信息,类下面使用三个引号中包含的内容。 -
__class__()方法:
输出当前实例的类名。 -
__dict__()方法:
输出类中的属性。 -
__getitem__()方法:
返回键对应的值,使用[]
来获取实例属性时会调用。 -
__setitem__()方法:
设置给定键的值,使用[]
设置实例属性时会调用。 -
__delitem__()方法:
删除给定键对应的元素。 -
__contains__()方法:
判断类中是否包含指定元素。 -
__getattribute__()方法:
当程序访问对象的name属性时会被调用 -
__getattr__()方法:
内置使用点号获取实例属性,如s.name
,会先从object
里的__getattribute__
中找,第二步从对象的属性中找,第三步从当前类中找,第四步从父类中找,第五步从__getattr__()
中找,如果没有则直接抛出异常。 -
__setattr__()方法:
使用点号设置类实例属性时会调用。 -
__delattr__()方法:
删除指定属性。
3.3.2 实现迭代器常用特殊方法
__iter__()方法:
该方法返回一个迭代器,迭代器必须包含一个__next__()
方法,该方法返回迭代器的下一个元素。__reversed__()方法:
该方法主要为内建函数reversed()
反转函数提供支持,当程序调用reversed()
函数对指定迭代器执行反转时,是通过该方法实现的。
# 实现斐波那契数列
class Fib:
def __init__(self, length):
self.first = 0
self.second = 1
self.__len = length
def __next__(self):
if self.__len == 0:
raise StopIteration
self.first, self.second = self.second, self.first + self.second
self.__len -= 1
return self.first
def __iter__(self):
return self
fib = Fib(10)
for el in fib:
print(el, end=" ")
3.3.3 运算符重载的特殊方法
__add__()方法:
加法运算,为”+“运算符提供支持__sub__()方法:
减法运算,为“-”运算符提供支持__mul__()方法:
乘法运算,为“*”运算符提供支持__truediv__()方法:
除法运算,为“/”运算法提供支持__floordiv__()方法:
整除运算,为“//”运算符提供支持__mod__()方法:
求余运算,为“%”运算符提供支持__divmod__()方法:
求余运算,为divmod
运算符提供支持__pow__()方法:
乘方运算,为"**"运算符提供支持__lshift__()方法:
左移运算符,为“<<”运算符提供支持__rshift__()方法:
右移运算符,为“>>”运算符提供支持__and__()方法:
按位与运算,为“&”运算符提供支持__or__()方法:
按位或运算,为“|”运算符提供支持__xor__()方法:
按位异或运算,为”^“运算符提供支持
另外还有一些前面带’r’的重载运算符的方法,用于计算将该对象放在符号后面时的会调用响应的方法。
还有前面带’i’的重载运算符的方法,用于带赋值运算的符号,如+=、*=
等带等号的运算符。
3.3.4 比较运算符重载的方法
__lt__()方法:
为“<”运算符提供支持__le__()方法:
为“<=”运算符提供支持__eq__()方法:
为“==”运算符提供支持__ne__()方法:
为“!=”运算符提供支持__gt__()方法:
为“>”运算符提供支持__ge__()方法:
为“>=”运算符提供支持
3.3.5 与单目运算符相关的特殊方法
__neg__()方法:
为单目求负运算符提供支持__pos__()方法:
为单目求正运算符提供支持__invert__()方法:
为单目取反运算符提供支持。
3.3.6 与类型转换相关的特殊方法
__bytes__()方法:
对应于调用内置函数bytes()
将该对象转换成字节内容,该方法返回bytes
对象。__complex__()方法:
对应于调用内置的complex()
函数将该对象转换成复数形式,该方法返回一个complex
对象。__float__()方法:
对应于调用内置的float()
函数将对象转换成浮点数,该方法返回float
对象。__int__()方法:
对应于调用内置的int()
函数将对象转换为整数,该方法返回一个int
对象。__str__()方法:
对应于调用内置的str()
函数将该对象转换成一个字符串。
3.3.7 与常见内建函数相关的特殊方法
__format__()方法:
对应于调用内置的format()
函数将对象转换成格式化字符串。__hash__()方法:
对应于调用内置的hash()
函数来获取该对象的hash值。__abs__()方法:
对应于调用内置的abs()
函数返回绝对值。__round__()方法:
对应于调用内置的round()
函数执行四舍五入整数。__trunc__()方法:
对应于调用内置的trunc()
函数执行截断取整。使用int()
函数将对象转为整数的时候,如果没有定义__int__()
方法,而是提供了__trunc__()
方法,底层将由__trunc__()
方法提供支持。__floor__()方法:
对应于调用内置的floor()
函数执行向下取整。__ceil__()方法:
对应于调用内置的ceil()
函数,执行向上取整。
4. 类的特性
4.1 封装
封装(Encapsulation)指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类中所提供的方法来实现对内部信息的操作和访问。
这也就是前面说过的私有属性和私有方法,是使用前置双下划线完成对属性和方法的私有化,也就是封装。可以通过提供接口来实现对私有属性或私有方法的访问。
4.2 继承
继承是一种创建新类的方式,在python中,新建类可以继承一个或者多个父类, 父类又可以称为基类或者超类,新建的类称为派生类或者子类。
继承分为单继承和多继承
4.2.1 单继承
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("{} is eating".format(self.name))
def run(self):
print("{} is running".format(self.name))
class Student(Person): # 继承Person类
def __init__(self, name, age, student_no):
super(Student, self).__init__(name, age)
self.sno = student_no
def take_exam(self):
print("{}(sno:{}) is taking exam".format(self.name, self.sno))
student = Student("Meng", 14, 20201234)
student.eat()
student.take_exam()
print(Student.__bases__)# 打印Student的父类
4.2.2 多继承
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("{} is eating".format(self.name))
def run(self):
print("{} is running".format(self.name))
class YoungPioneer:
def wear_red_scarf(self):
print("A student should wear red scarf.")
class Pupil(Person, YoungPioneer): # 继承Person, YoungPioneer
def __init__(self, name, age, student_no):
super(Pupil, self).__init__(name, age)
self.sno = student_no
def take_exam(self):
print("{}(sno:{}) is taking exam".format(self.name, self.sno))
student = Pupil("Meng", 10, 20201234)
student.eat()
student.take_exam()
student.wear_red_scarf()
print(Pupil.__bases__)
继承顺序,在python3
中均按照广度优先搜索。
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.mro())# 打印继承顺序
#=====output======
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
4.3 多态
多态指的是不同的对象调用相同的代码,产生不同的效果,提高代码的灵活性
class Animal:
def move(self):
raise NotImplementedError("子类中没有实现该方法!")
class Dog(Animal):
def move(self):
print("狗在狂奔!")
class Bird(Animal):
def move(self):
print("鸟在飞翔!")
dog = Dog()
bird = Bird()
dog.move()
bird.move()# 同一类型的对象调用同一个方法,表现出不同的行为,这就是多态
这样的话Animal
类就相当于一个抽象类,继承该类的对象需要实现其中没有实现的方法,也可以重写抽象类中已经实现的方法。
抽象类的另一种定义方式如下:
from abc import ABCMeta
from abc import abstractmethod
class Animal(metaclass=ABCMeta):
@abstractmethod
def move(self):
pass
class Dog(Animal):
def move(self):
print("狗在狂奔!")
使用多态的优点:
- 增加代码的灵活度。
- 以继承和重写父类的方法为前提。
- 调用方法,不会影响到类的内部设计。