Python和java一样,是面向对象的编程语言,也有类和对象,且概念是一样的。
Python中所有的类型都是一个类,所有的变量也都是对象的引用
在类中的函数叫方法,而独立的函数就叫函数,本质上没有区别
创建一个类的语法如下
class 类名: 类的成员
类名开头字母要大写,这虽然不是语法要求,但这是程序员间约定俗成的,注意冒号与缩进
创建一个对象类似于函数的调用,如下
class A: def fun(self): print('我真帅') x = A() x.fun()
其实这种方式相当于调用了A类的构造方法
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
类,类对象和实例对象其实都是不同的东西,我们一个个解释
当生成实例对象后,他会自动绑定类对象所拥有的方法和变量,但这些并不会作为实例对象的成员
当我们通过实例对象来调用绑定的方法时,这个对象会自动作为第一个参数,也就是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,...)
使用多重继承时,如果两个父类有重名的方法,则继承时类名靠前的会覆盖掉类名靠后的
注意:当在父类里写了构造方法,而在子类中又写了一个构造方法,那么父类的构造方法将会被覆盖,不会执行
解决这个问题有如下两种方法
使用类对象来调用类的方法,其语法规则如下
类对象.方法名(对象)
例如
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方法的实参
使用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
继承Parent2
,Child
再继承Parent1
。而super().act()
就是调用Parent1
的act()
,也就是说super()
可以调用第一个继承的类的方法
从这两个例子可以很明显的看出这两种方法的优缺点
issubclass(class, classinfo)
:class
和classinfo
是类对象,如果class
是classinfo
的一个子类,返回True
,否则,返回False
,有以下几个注意点。
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,有以下几个注意点
object
是一个类对象,则永远返回Falseissubclass
,classinfo
也可以是一个由类对象组成的元组,判断的机制也相似,不再赘述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
函数和方法的本质相同,但有一些区别
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'>