python类和对象

2021/5/31 20:25:17

本文主要是介绍python类和对象,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

python类和对象

Python和java一样,是面向对象的编程语言,也有类和对象,且概念是一样的。

Python中所有的类型都是一个类,所有的变量也都是对象的引用

在类中的函数叫方法,而独立的函数就叫函数,本质上没有区别


创建类

创建一个类的语法如下

class 类名:
	类的成员

类名开头字母要大写,这虽然不是语法要求,但这是程序员间约定俗成的,注意冒号与缩进


创建对象

创建一个对象类似于函数的调用,如下

class A:
    def fun(self):
        print('我真帅')

x = A()
x.fun()

其实这种方式相当于调用了A类的构造方法


self

Python中的self和java的this指针很相似,都是指代当前的对象

通常情况下,类里面的方法第一个参数必须是self,但在通过对象使用方法时,Python会自动为self赋值,因此我们不用亲自为self赋值

其实在类的方法中,第一个参数就是指代当前类的实例对象,并不一定要使
用self,使用self是程序员约定俗成的做法


构造方法__init__

Python的构造函数叫__init__,同样第一个参数为self,在实例化类生成对象时会自动调用,没有也不能有返回值

默认的构造函数里只有一个self参数,里面什么也没有

在生成对象时,类名后面的括号里就是构造函数的参数,语法如下

对象名 = 类名(构造函数的参数)

举个例子

class A:
    x = '123'
    def __init__(self,s):
        self.x = s

a = A('456')
print(a.x)# 456

私有成员

与java不同,Python中并没有private,public这两个关键字,但这不意味着它所有成员都是公有的

定义一个私有成员,只需要在其名字前面加上两个下划线__即可,这样,就无法从外部来直接访问这个成员了

注意:Python的私有其实是一种伪私有,本质上只是自动把成员的名字改成了_类名__成员名,因此我们完全可以通过改变后的名字来访问成员,举个例子

class A:
    __x = '123'
    
a = A()
try:
    print(a.__x)
except AttributeError:
    print('没有找到__x')
try:
    print(a._A__x)
except AttributeError:
    print('没有找到_A__x')
# output: 
# 没有找到__x
# 123

类,类对象和实例对象

类,类对象和实例对象其实都是不同的东西,我们一个个解释

  1. 类:类是一个抽象的概念
  2. 类对象:当定义完类后,由于在Python中所有的东西都是对象,所以类也是一个对象,对象名就是类名
  3. 实例对象:用类对象的构造方法来生成的对象就叫实例对象

绑定

当生成实例对象后,他会自动绑定类对象所拥有的方法和变量,但这些并不会作为实例对象的成员

当我们通过实例对象来调用绑定的方法时,这个对象会自动作为第一个参数,也就是self

当我们通过类对象来调用方法(非绑定调用)时,那他就跟调用函数一样,有几个参数就要传入几个参数,例如

当这个类对象被删除时,它的成员方法并不会被删除,也就是说它的实例对象依然可以使用成员方法和变量

class A:
    email = '@.com'
    def __init__(self,a):
        self.name = '我是'+a
    def fun1():
        print('我是类A的方法')
    def fun2(self):
        print('我通过'+self.name+'调用fun2')
        
a = A('帅')
A.fun1()
A.fun2(a)
del A
a.fun2()
print(a.email)
# output:
# 我是类A的方法
# 我通过我是帅调用fun2
# 我通过我是帅调用fun2
# @.com

继承

与java的继承一样,可以继承父类所有的成员,其语法如下

class 子类名(父类名):
	类成员

如果子类中定义了与父类同名的方法或属性,则会自动覆盖掉父类对应的方法或属性,举个例子

class Parent:
    name = '我是父类的属性'
    def act1(self):
        print('正在调用父类的方法')
    def act2(self):
        print('我是父类的方法')
        
class Child(Parent):
    def act2(self):
        print('我是子类的方法')

p = Parent()

c = Child()
print(c.name)
c.act1()
p.act2()
c.act2()
# output:
# 我是父类的属性
# 正在调用父类的方法
# 我是父类的方法
# 我是子类的方法

Python也支持多重继承,一个子类可以拥有多个父类,其语法如下

class 子类名(父类名1,父类名2,父类名3,...)

使用多重继承时,如果两个父类有重名的方法,则继承时类名靠前的会覆盖掉类名靠后的

注意:当在父类里写了构造方法,而在子类中又写了一个构造方法,那么父类的构造方法将会被覆盖,不会执行

解决这个问题有如下两种方法

  1. 使用类对象来调用类的方法,其语法规则如下

    类对象.方法名(对象)
    

    例如

    class Parent1:
        name = '我是父类1'
        def __init__(self):
            print('父类1的构造方法')
    
    class Parent2:
        name = '我是父类2'
        def __init__(self):
            print('父类2的构造方法')
            
    class Child(Parent1,Parent2):
        name = '我是子类'
        def __init__(self):
            Parent1.__init__(self)
            Parent2.__init__(self)
            print('子类的构造方法')
        def act(self):
            print(self.name)
    
    
    c = Child()
    p1 = Parent1()
    Child.act(p1)
    # output:
    # 父类1的构造方法
    # 父类2的构造方法
    # 子类的构造方法
    # 父类1的构造方法
    # 我是父类1
    

    从这个例子可以看出,类方法的self参数并不关心你是不是这个类的实例对象,或者说Python中所有的函数形参都不关心类型,所以p1也可以作为Child中的act方法的实参

  2. 使用super()函数,super()函数可以调用父类的一个方法,其语法规则如下

    super().父类的方法名(参数)
    

    例如

    class Parent1:
        name = '我是父类1'
        def __init__(self):
            print('父类1的构造方法')
    
    class Parent2:
        name = '我是父类2'
        def __init__(self):
            print('父类2的构造方法')
        def act(self):
            print(self.name)
            
    class Child(Parent1,Parent2):
        
        def __init__(self):
            super().__init__()
            print('子类的构造方法')
            super().act()
    
    c = Child()
    # output:
    # 父类1的构造方法
    # 子类的构造方法
    # 我是父类1
    

    从这个例子可以看出,多重继承的机制似乎是前面的继承后面的,也就是Parent1继承Parent2Child再继承Parent1。而super().act()就是调用Parent1act(),也就是说super()可以调用第一个继承的类的方法

从这两个例子可以很明显的看出这两种方法的优缺点

  • 第一种优点:使用起来比较灵活,想调用哪个父类的方法就调用哪个父类的方法
  • 第一种缺点:我们在写的时候需要使用到父类名,当我们该表父类名的时候,子类的代码也需要改变,比较麻烦
  • 第二种优点:写起来方便,改起来也方便,当父类名改变时,子类的代码不需要变
  • 第二种缺点:当多个父类有重名方法时,由于靠后继承的会被靠前的覆盖,所以super只会执行靠前的父类的方法

有关类的内置函数

  • issubclass(class, classinfo)classclassinfo是类对象,如果classclassinfo的一个子类,返回True,否则,返回False,有以下几个注意点。

    1. 一个类被认为是自身的子类
    2. classinfo可以是一个由类对象组成的元组,则会从前往后找class的父类,只要找到,就会停止查找,并返回True,若classinfo查找的时候遇到不是类对象的元素,则会报TypeError
    class A:
        pass#pass是一个占位符,什么都不做,只是美观
    class B(A):
        pass
    class C:
        pass
    
    a = A()
    print(issubclass(B,A))
    try:
        print(issubclass(B,(C,A,a)))#遇到a之前就找到了父类,停止了查找,所以没报错
        print(issubclass(B,(C,a,A)))
    except TypeError:
        print('classinfo中存在非类对象元素')
    # output
    # True
    # True
    # classinfo中存在非类对象元素
    
  • isinstance(object, classinfo):判断实例对象object是不是类对象classinfo的的实例对象,若是,则返回True,否则,返回False,有以下几个注意点

    1. 如果object是一个类对象,则永远返回False
    2. 类似于issubclassclassinfo也可以是一个由类对象组成的元组,判断的机制也相似,不再赘述
    3. 子类的实例对象也属于父类的实例对象,但反过来不成立
    class A:
        pass
    class B(A):
        pass
    class C:
        pass
    
    a = A()
    b = B()
    print(isinstance(a,A))
    print(isinstance(a,(C,A,b)))
    print(isinstance(a,B))
    print(isinstance(b,A))
    # output
    # True
    # True
    # False
    # True
    
  • hasattr(object, name):判断实例对象object中是否存在name属性,如果有,则返回True,否则,返回False,例如

    class C:
        x = "我是C"
        def y(self):
            pass
    c = C()
    print(hasattr(c, 'x'))
    print(hasattr(c, 'y'))
    print(hasattr(c, 'z'))
    # output:
    # True
    # True
    # False
    
  • getattr(object, name[, default]):如果实例对象object中存在name属性,则返回其值,否则,如果有参数default,则返回default,如果没有,就会报AttributeError,例如

    class C:
        x = "我是C"
        def y(self):
            pass
    c = C()
    print(getattr(c, 'x'))
    print(getattr(c, 'y'))
    print(getattr(c, 'z','z不存在'))
    # output:
    # 我是C
    # <bound method C.y of <__main__.C object at 0x0000028DCDD63248>>
    # z不存在
    
  • setattr(object, name, value),将value对象赋值给实例对象object里的name属性,如果没有name属性,则会创建该属性,该函数没有返回值,例如

    class A:
        pass
    class C:
        x = "我是C"
        def y(self):
            pass
    c = C()
    a = A()
    setattr(c,'x','哈哈哈')
    setattr(c,'y','嘻嘻嘻')
    print(getattr(c, 'x'))
    print(getattr(c, 'y'))
    if(not hasattr(c,'z')):
        setattr(c, 'z', a)
        print(getattr(c, 'z'))
    else:
        print(getattr(c, 'z'))
    # output:
    # 哈哈哈
    # 嘻嘻嘻
    # <__main__.A object at 0x0000015B48F53348>
    
  • delattr(object, name):删除object对象中的name属性,若不存在,则报AttributeError,注意,这里不会把绑定的属性算在内,因为绑定的属性无法通过实例对象来删除,该函数没有返回值,例如

    class C:
        x = "我是C"
        def __init__(self):
            self.z = 'zzz'
        def y(self):
            pass
    
    c = C()
    try:
        delattr(c, 'x')
    except:
        print('无法删除x')
    print(getattr(c, 'x', 'x不存在'))
    delattr(c, 'z')
    print(getattr(c,'z','z不存在'))
    # output:
    # 无法删除x
    # 我是C
    # z不存在
    
  • property(fget=None, fset=None, fdel=None,doc=None):这个函数很特殊,它的返回值是一个对象,而fget,fset,fdel,doc都是一些方法,我们可以通过对返回的这个对象进行特定的操作来调用这些方法,例如

    class A:
        def __init__(self):
            self.name = '王'
        def getName(self):
            return self.name
        def setName(self, name):
            self.name = name
        def delName(self):
            del self.name
        x = property(getName,setName,delName)
    
    a = A()
    print(a.x)
    a.x = '李'
    print(a.x)
    del a.x
    try:
        print(a.name)
    except AttributeError:
        print('a.name不存在')
    a.x = '张'
    print(a.x)
    print(a.name)
    # output:
    # 王
    # 李
    # a.name不存在
    # 张
    # 张
    

    我们可以看出,这种方式类似于操作符重载,对操作符进行了重新定义,所以当我们使用=时,会调用setName这个方法,当我们使用del时,会调用delName这个方法,使用property可以让你的程序更加简洁美观规范,同时维护更加方便


析构方法__del__

当一个对象没有引用指向它时,Python的垃圾回收机制会自动调用对象的析构方法,同时释放掉该对象所占用的空间,没有返回值,例如

>>> class A:
    def __init__(self):
        print('我是__init__')
    def __del__(self):
        print('我是__del__')

>>> a1 = A()
我是__init__
>>> a2 = a1
>>> a3 = a1
>>> del a2
>>> del a3
>>> del a1
我是__del__

创建对象方法__new__

在Python中,当你创建一个实例对象时,第一个调用的方法不是构造方法__init__,而是__new__,这个方法的语法如下

__new__(cls[, ...])

其中,cls是类对象,其返回值是一个对象,返回值默认是cls的实例对象

这个方法我们通常不用重写,只有当我们继承了一个系统内置的类时,且我们需要对其进行修改时,我们可以考虑重写它来实现需求,例如

class A(str):
    def __new__(cls,string):
        string = string.upper()
        return str.__new__(cls,string)

a = A('i love China')
print(a)# I LOVE CHINA

其他细枝末节

  • 函数和方法的本质相同,但有一些区别

    • 不同点是:方法是在类中定义的,函数是在类外面定义的,要使用必须通过一个对象去调用,方法的第一个参数必须是self,如果通过实例对象调用,则Python会自动为self赋值,如果通过类对象调用,则你必须自己给self赋值,且函数和方法的类型是不同的,方法可以访问一个对象里的私有成员,函数不行。
    • 相同点的是:他们执行的机制是相同的,包括参数传递机制,语法也类似,函数和方法的名字都是引用,类似于变量,指向了代码段,当我们在名字后加()时,他们会跑去代码段执行,并返回结果,例如
class A:
    def fun(self):
        print('ko no 方法 da')

def fun():
    print('ko no 函数 da')

x = A()
print(type(x.fun))
print(type(fun))
print(type(A.fun(x)))
print(type(fun()))
# output:
# <class 'method'>
# <class 'function'>
# ko no 方法 da
# <class 'NoneType'>
# ko no 函数 da
# <class 'NoneType'>


这篇关于python类和对象的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程