类和对象

Python被称为面向对象语言,所以面向对象技术支持非常完整,例如,类、接口、继承、封装、多态等。

8.1 对象的魔法
对象可以看作数据以及可以操作这些数据的一系列方法的集合。为了区分全局函数,将这些写在类中的函数称为方法。想要访问这些类中的函数,必须要对类实例化,实例化后的产物被称为对象。实例化后,调用方法时需要先指定对象名称,然后才可调用这些方法。

面向对象的三大特征:

继承:当前类从其他类获得资源(数据和方法),以便更好的重用代码,并且可以描述类与类之间的关系。
封装:对外部世界隐藏对象的工作细节。
多态:只有在程序运行时才确定具体的类,从而导致该引用调用的具体方法随之改变,让程序可以选择多个运行状态,这就是多态。
python中没有接口的概念,想要使用接口,就直接使用对象好了,而且假定要调用的对象成员都存在。( 当一个方法在很多类中有不同的体现的时候,这个时候就可以将这个方法抽象出来做成一个接口,即一组规范 )。

8.2 类
8.2.1 创建自己的类
创建一个类,以及利用这个类创建两个对象,并调用其中的方法。

# 创建一个Person类
class Person:
# 定义setName方法
def setName(self, name):
self.name = name
# 定义getName方法
def getName(self):
return self.name
# 定义greet方法
def greet(self):
print(“Hello, I’m {name}.”.format(name = self.name))

# ————-对类实例化——————-
# 对类实例化,实例化后的产物就是对象,即引用类时指定了一个变量,并分配了一块内存
person1 = Person()
person2 = Person()

# ————-给类中的变量赋值————–
# 调用类对象中的setName方法
person1.setName(“Bill Gates”)
# 调用类对象中的name属性
person2.name = “Bill Clinton”

# ————-调用类中的方法—————-
# 两种等价调用方式
print(person1.getName())
person1.greet()
# 两种等价调用方式
print(person2.name)
Person.greet(person2)

知识点:

python类使用class关键字定义,类名直接跟在class关键字的后面。
类名也是一个代码块,所以类名后面要跟着一个冒号(:).。
类中的方法其实就是函数,定义方式完全一样。
每一个方法的*个参数都是self,其实这是必须的。任何方法必须至少指定一个self参数,如果方法包含多个参数,*个参数将作为self参数使用。在调用方法时,这个参数值不需要自己传递。系统会将方法所属的对象传入这个参数。在方法内部可以利用这个参数调用对象本身的资源,如属性、方法等。
通过self参数添加的name变量是person类的属性,可以在外部访问。上例子中设置了person2对象的name属性值,与调用person.setName方法的效果完全相同。
调用对象的方法有两种方式。一种是直接通过对象变量调用方法,另一种是通过调用方法,并且将相应的对象传入方法的第1个参数。例如 Person.greet(person2)的方式调用了person2对象中的greet方法。
8.2.2 方法和私有化
python 类默认情况下,所有的方法都可以被外部访问。在python类中并没有提供private或类似的关键字将方法私有化,但可以迂回解决。方法私有化:只有类的内部方法才能访问私有化的方法,通过正常的方法是无法访问对象的私有化方法的,使用反射技术的除外。

在python类的方法名前面加双下划线(__)可以让该方法在外部不访问。

class person:
# 在外部可以访问
def method1(self):
print(“method1”)

# 在外部不可访问

def __method2(self):
print(“method2”)

def method3(self):
print(“method3”)
# 内部正常引用
self.__method2()

p = person()
p.method1()
p.method3()
p._person__method2() #外部正常引用
p.__method2()

p.__method2() 抛出异常,找不到相关类名

Traceback (most recent call last):
File “C:/Users/UFO/PycharmProjects/fullstack1/test.py”, line 14, in <module>
p.__method2()
AttributeError: ‘person’ object has no attribute ‘__method2’

原理分析:其实 __method2() 方法也不是*对不可访问的。python编译器在编译源代码时,并没有将 __method2() 方法私有化,而是一旦遇到方法名以双下划线开头的方法,就会将方法名改成 _ClassName__methodName 的形式。其中,ClassName表示该方法所在的类名,methodName表示方法名。

class MyClass:
def getName(self):
return self.name
def setName(self, name):
self.name = name
# 直接调用私有方法
self.__outName()

def __outName(self):
print(“Name = {}”.format(self.name))

myClass = MyClass()
# inspect模块中的getmembers方法可以输出myClass类中的所有成员方法,并输出方法名
import inspect
methods = inspect.getmembers(myClass, predicate=inspect.ismethod)
print(methods)

for method in methods:
print(method[0])

print(“————“)
myClass.setName(“Bill”)
print(myClass.getName())
myClass._MyClass__outName()
print(myClass.__outName())

8.2.3 类代码块
class语句与for、while语句一样,都是代码块,这就意味着,定义类其实就是执行代码块。

class person:
print(“person”)

# 输出结果:person

class MyClass:
print(“MyClass”)
count = 0
def counter(self):
self.count += 1

my = MyClass()
my.counter()
print(my.count)
my.counter()
print(my.count)
my.count = “abc”
print(my.count)
my.name = “Hello”
print(my.name)

执行结果

MyClass
1
2
abc
Hello

8.2.4 类的继承
所谓类的继承,就是指一个类(子类)从另外一个类(父类)中获得了所有的成员。父类的成员可以在子类中使用,就像子类本身的成员一样。(私有方法的调用另外考虑,取决于调用方法的名称方式)

class ParentClass:
name = 30
def method1(self):
print(“method1”)

# 类继承的定义方式
class ChildClass(ParentClass):
def method2(self):
print(“method2”)
print(self.name)

# 通过子类调用父类的方法
child = ChildClass()
child.method1()
child.method2()

8.2.5 检测继承关系
在很多场景中,需要知道一个类A是否是从另外一个类B继承的,这种效验主要是为了调用B类中的成员(方法和属性)。关键是要判断B是否为A的父类。

如果要判断类与类之间的关系可以使用 issubclass函数,该函数接收两个参数,第1个参数是子类,第2个参数是父类。如果第1个参数指定的类与第2个参数指定的类确实是继承关系,那么该函数返回True,否则返回False。

如果想要获得已知类的父类(可能有多个父类),可以直接使用 bases ,这是类的一个特殊属性。

也可以使用 isinstance 函数检测一个对象是否是某一个类的实例。isinstance函数有两个参数,第1个参数是要检测的对象,第2个参数是一个类。如果第1个参数指定的对象是第2个参数指定的类的实例,那么该函数返回True,否则返回False。

# 父类(爷爷)
class MyParentClass:
def method(self):
return 50
# 子类(爸爸)
class ParentClass(MyParentClass):
def method1(self):
print(“method1”)
class MyClass:
def method(self):
return 40
# 子类(儿子)
class ChildClass(ParentClass):
def method2(self):
print(“method2”)

# 判断继承关系,可以判断包括多层的继承关系
print(issubclass(ChildClass, ParentClass))
print(issubclass(ChildClass, MyClass))
print(issubclass(ChildClass, MyParentClass))

# 仅仅得到上一层的父类名称,不会输出多层父类名称
print(ChildClass.__bases__)
print(ParentClass.__bases__)

# 判断是否是类的实例,可以判断多层继承的类的实例关系
child = ChildClass()
print(isinstance(child, ChildClass))
print(isinstance(child, ParentClass))
print(isinstance(child, MyParentClass))
print(isinstance(child, MyClass))

8.2.6 多继承
python支持多继承。想要为某个类指定多个父类,需要在类名称后边的圆括号中设置。多个父类名之间用逗号(,)分隔。

如果多个父类中有相同的成员,例如,在两个或两个以上父类中有同名的方法,那么会按照父类书写的顺序继承。也就是说,写在前面的父类会覆盖写在后面的父类同名的方法。在python类中,不会根据方法参数个数和数据类型进行重载。

class Calculator:
def calculate(self,expression):
self.value = eval(expression)
def printResult(self):
print(“result:{}”.format(self.value))

class MyPrint:
def printResult(self):
print(“计算结果:{}”.format(self.value))
def aa(self,a):
return 30

# Calculator 在 MyPrint 前面,所以Calculator中的printResult方法会生效
class NewCalculator(Calculator, MyPrint):
# 如果类中没有代码,需要添加 pass
pass

# MyPrint 在 Calculator 前面,所以MyPrint中的printResult方法会生效
class NewCalculator1(MyPrint,Calculator):
# 如果类中没有代码,需要添加 pass
pass

# 多继承,并调用父类方法,优先的同名方法
calc = NewCalculator()
calc.calculate(“1 + 3 * 5”)
calc.printResult()
# 仅仅输出上层继承的多个类的名称,不是输出多层继承的父类名
print(NewCalculator.__bases__)

# 多继承,并调用父类方法,优先的同名方法
calc1 = NewCalculator1()
print(NewCalculator1.__bases__)
calc1.calculate(“1 + 3 * 5”)
calc1.printResult()

注意:多继承会提高代码重用率,但也会增加代码复杂度。

8.2.7 接口
在很多面向对象语言中都有接口的概念。接口其实就是一个规范,指定了一个类中有哪些成员。接口也经常用在多态中,一个类可以有多个接口,也就是多个规范。不过python语言中并没有这些东西,在调用一个对象时,就假设这个方法在对象中存在。当然,更稳妥的方法就是在调用方法之前先使用hasattr函数检测一下,如果方法在对象中存在,该函数返回True,否则返回False。

还可以使用getattr函数实现同样的功能。该函数有三个参数,前2个参数与hasattr函数完全一样(对象、成员),当第2个参数指定的成员不存在时,getattr函数会返回第3个参数指定的默认值。

与getattr函数对应的是setattr函数,第3个参数是用来设置对象中成员的值。(有则更新,无则添加)

class MyClass:
def method1(self):
print(“method1”)
def default(self):
print(“default”)

my = MyClass()

# my对象中是否存在method1方法
if hasattr(my, ‘method1′):
my.method1()
else:
print(“method2方法不存在”)

# my对象中是否存在method2方法
if hasattr(my,’method2’):
my.method2()
else:
print(“method2方法不存在”)

# my对象中method2方法不存在,则返回”default”值
method = getattr(my, ‘method2’,my.default)
method()

# 对象中成员不存在,则添加成员
def method2():
print(“动态添加的method2”)
setattr(my, ‘method2’, method2)
my.method2()