Python的面向对象编程

1、类的定义(class)

在Python中,定义类是通过class关键字,类名通常是大写开头的单词。语法格式如下:

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

类实例化后,可以使用其属性,实际上,创建一个类之后,可以通过类名访问其属性。

1.1、构造方法(__init__(self))

类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用,像下面这样:

def __init__(self):
    self.data = []

如下实例化类 MyClass,对应的 __init__() 方法就会被调用:

x = MyClass()

注意,__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。当然, __init__() 方法也可以有其他参数,参数通过 __init__() 传递到类的实例化操作上。在构造实例时,self参数不需要传,Python解释器自己会把实例变量传进去。

示例:

#!/usr/bin/python3
 
class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart
x
= Complex(3.0, -4.5) print(x.r, x.i) # 输出结果:3.0 -4.5

类的方法与普通的函数只有一个特别的区别:它们必须有一个额外的第一个参数名称(我们习惯将它命名为 self,但不是必须叫这个名字),除此之外,类的方法和普通函数没有什么区别。self代表的是类的实例,而非类。

1.2、创建一个类实例

类对象支持两种操作:属性引用和实例化。属性引用使用和 Python 中所有的属性引用一样的标准语法:obj.name

类对象创建后,类命名空间中所有的命名都是有效属性名。所以如果类定义是这样:

#!/usr/bin/python3
 
class MyClass:
    """一个简单的类实例"""
    i = 12345
    def f(self):
        return 'hello world'
 
# 实例化类
x = MyClass()
 
# 访问类的属性和方法
print("MyClass 类的属性 i 为:", x.i)
print("MyClass 类的方法 f 输出为:", x.f())

可以自由地给一个实例变量绑定属性,比如,给实例 x 随便绑定一个name属性:

x.name = 'Bart Simpson'
print(bart.name)   #'Bart Simpson'

和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。

1.3、实例属性和类属性

Python是动态语言,可以给类的实例绑定任意的属性。给实例绑定属性我们可以通过self变量,或者直接给类实例绑定也行:

class Student(object):
    def __init__(self, name):
        self.name = name

s = Student('Bob')
s.score = 90    #直接绑定一个属性

在一个类中定义的属性,这种属性是类属性,归类本身所有:

class Student(object):
    name = 'Student'     #类属性

类属性可以直接通过类来访问,也可以通过类实例来访问:

class Student(object):
...     name = 'Student'
...

s = Student() # 创建实例s
print(s.name) #Student 打印出name属性,因为实例并没有name属性,所以会继续查找class的name属性

print(Student.name) #Student  打印类的name属性

s.name = 'Michael'  # 给实例绑定name属性
print(s.name) #Michael 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性

print(Student.name) #Student 但是类属性并未消失,用Student.name仍然可以访问

del s.name # 如果删除实例的name属性
print(s.name) #Student 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了

在给一个类实例绑定属性时,建议不要使用和类属性相同的名字。

实例属性属于各个实例所有,互不干扰。类属性属于类所有,所有实例共享一个属性。建议不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。

1.4、类的方法

在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例。

#!/usr/bin/python3
 
#类定义
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
# 实例化类
p = people('runoob',10,30)
p.speak()    #输出  runoob 说: 我 10 岁。

1.5、私有属性和私有方法(以__开头的属性和方法)

在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。如果外部直接通过实例来访问私有属性和方法的话,代码将直接报错。

私有属性:以两个下划线开头(比如:__private_attrs)的属性为类的私有属性,该属性不能在类的外部被使用或直接访问,在类内部的方法中使用 self.__private_attrs 进行访问。

私有方法:以两个下划线开头(比如:__private_method)的属性为类的私有方法,该方法不能在类的外部被使用或直接访问,在类内部的方法中使用 self.__private_method 进行访问。

私有属性代码示例:

#!/usr/bin/python3
 
class JustCounter:
    __secretCount = 0  # 私有变量
    publicCount = 0    # 公开变量
 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print (self.__secretCount)
 
counter = JustCounter()
counter.count()
print (counter.publicCount)
print (counter.__secretCount)  # 这里将报错,实例不能访问私有变量

私有方法代码示例:

#!/usr/bin/python3
 
class Site:
    def __init__(self, name, url):
        self.name = name       # public
        self.__url = url   # private
 
    def who(self):
        print('name  : ', self.name)
        print('url : ', self.__url)
 
    def __foo(self):          # 私有方法
        print('这是私有方法')
 
    def foo(self):            # 公共方法
        print('这是公共方法')
        self.__foo()    # 可以通过类内部的方法来访问私有方法
 
x = Site('菜鸟教程', 'www.runoob.com')
x.who()        # 正常输出
x.foo()        # 正常输出
x.__foo()      # 访问私有方法这里将报错

1.5.1、通过实例直接访问私有变量

一般来说,私有变量是不能直接访问的,比如 __name,这是因为Python解释器对外把__name变量改成了_Student__name。但是实际上我们仍然可以通过_Student__name来访问__name变量:obj.__Student_name。

但是非常不建议这么做,因为不同版本的Python解释器可能会把__name改成不同的变量名。

2、继承

Python 同样支持类的继承。基类必须与派生类定义在一个作用域内。

派生类的定义如下所示:则此时基类 BaseClassName 必须和类 DerivedClassName 定义在同一个作用域内。

class DerivedClassName(BaseClassName1):
    <statement-1>
    .
    .
    .
    <statement-N>

除了直接使用类,还可以用表达式,基类定义在另一个模块中时这一点非常有用:

class DerivedClassName(modname.BaseClassName):

继承代码示例:

#!/usr/bin/python3
 
#类定义
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
#单继承示例
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #调用父类的构函
        people.__init__(self,n,a,w)
        self.grade = g
    #覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
 
 
 
s = student('ken',10,60,3)
s.speak()   #输出  ken 说: 我 10 岁了,我在读 3 年级

2.1、多继承

Python同样有限的支持多继承形式。继承了多个类的类定义形如下例:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

代码实例:

#!/usr/bin/python3
 
#类定义
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
#单继承示例
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #调用父类的构函
        people.__init__(self,n,a,w)
        self.grade = g
    #覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
 
#另一个类,多重继承之前的准备
class speaker():
    topic = ''
    name = ''
    def __init__(self,n,t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
 
#多重继承
class sample(speaker,student):
    a =''
    def __init__(self,n,a,w,g,t):
        student.__init__(self,n,a,w,g)
        speaker.__init__(self,n,t)
 
test = sample("Tim",25,80,4,"Python")
test.speak()   #方法名同,默认调用的是在括号中排前地父类的方法   输出:我叫 Tim,我是一个演说家,我演讲的主题是 Python

2.2、super() 函数

super() 函数是用于调用父类(超类)的一个方法。

class A:
     def add(self, x):
         y = x+1
         print(y)
class B(A):
    def add(self, x):
        super().add(x)
b = B()
b.add(2)  # 3

3、判断对象类型的方法

3.1、type()

判断对象类型可以使用type()函数,基本类型都可以用type()判断:

type(123)  #<class 'int'>

type('str')  #<class 'str'>

type(None)  #<type(None) 'NoneType'>

#函数和类也可以用type()判断:
type(abs)  #<class 'builtin_function_or_method'>
type(a)     #<class '__main__.Animal'>

type()函数返回的返回对应的Class类型。我们可以直接比较两个变量的type类型是否相同:

type(123)==type(456)  #True

# 判断基本数据类型可以直接写int,str等
type(123)==int  #True 
type('abc')==str  #True 

#如果要判断一个对象是否是函数可以使用types模块中定义的常量:
import types
def fn():
... pass
...
type(fn)
==types.FunctionType #True type(abs)==types.BuiltinFunctionType #True type(lambda x: x)==types.LambdaType #True type((x for x in range(10)))==types.GeneratorType #True

3.2、使用 isinstance(实例, 类)

对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数:

#假设继承关系:object -> Animal -> Dog -> Husky

a = Animal()
d = Dog()
h = Husky()

isinstance(h, Dog)  #true
isinstance(h, Animal)  #true
isinstance(d, Dog) and isinstance(d, Animal)  #true
isinstance(d, Husky)  #false

isinstance('a', str)  #True
isinstance(123, int)  #True
isinstance(b'a', bytes)  #True

使用 isinstance() 还可以判断一个变量是否是多个类型中的其中一种。比如下面的代码就可以判断是否是list或者tuple:

isinstance([1, 2, 3], (list, tuple))  #True
isinstance((1, 2, 3), (list, tuple))  #True
原文地址:https://www.cnblogs.com/wenxuehai/p/14295772.html