Python3类

关于Pyhton3中类相关知识,例如抓狂的self!!!

0 继承/重写/Super

继承

1
2
3
4
5
6
7
8
9
10
11
12
class Parent:
def Family(self):
print("we are family")

class Child(Parent):
pass

father = Parent()
son = Child()

father.Family()
son.Family()

​ 在这里,Parent 类是父类,Child 类是子类,但是通过定义,继承了父类。所以在后面实例化之后,son 依然可以调用父类的函数。最后的运行结果如下:

重写(Override)

​ 子类除了可以继承父类的函数,也能够对父类函数进行重写

1
2
3
4
5
6
7
8
9
10
11
12
13
class Parent:
def Family(self):
print("we are family")

class Child(Parent):
def Family(self):
print("hey,i am son")

father = Parent()
son = Child()

father.Family()
son.Family()

Super

​ Super,超类,当既重写了同名的父类函数也在重写时调用了父类函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Parent:
def Family(self):
print("we are family")

class Child(Parent):
def Family(self):
print("hey,i am son")
super(Child,self).Family()
print("again, i am son")

father = Parent()
son = Child()

father.Family()
son.Family()

特性(property)

property使得类函数成为一种属性,能够像属性一样直接访问,访问时就默认执行该函数并返回值

但是,和真正的属性还是存在本质区别的,比如此时无法被赋值,只能根据其他属性的变化来更新

为啥要用他呢

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开

但是,python并没有在语法上C中的私有/公有/保护(private/public/protect)内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import math
class Circle:
def __init__(self,radius): #圆的半径radius
self.radius=radius

@property
def area(self):
return math.pi * self.radius**2 #计算面积

@property
def perimeter(self):
return 2*math.pi*self.radius #计算周长

c=Circle(10)
print(c.radius)
print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter) #不用像函数一样调用,c.perimeter()
'''
输出结果:
314.1592653589793
62.83185307179586
'''

静态方法(staticmethod)

主要就是可以不实例化类而直接调用函数,但是有点奇怪,好像不用静态方法也可以实现呀,如下图:

TIM截图20190311084538

有博客提供了一个应用场景,提到需要采用多种不同的方式创建实例时,而我们只有一个init函数,此时就可静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@staticmethod
def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
t=time.localtime() #获取结构化的时间格式
return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
@staticmethod
def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
t=time.localtime(time.time()+86400)
return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)

类方法(classmethod)

1 为什么要加Object呢

1.1 Object?

python中的type和object详解

1.2 为啥?

​ Python2时,都建议在类声明的时候加上object,其实就是继承,使其具有更多的操作对象,而在Python3中,已经默认加载了Object了
​ Python2

TIM截图20190308105428

​ Python3

TIM截图20190308105154

1.3 经典类/新式类

​ 其实还有个小问题,就是在Python2中,存在多重继承时的继承方式寻找问题,如果是不继承object的则是经典类,如果继承了object则为新式类,二者的具体区别如下图所示,而Python3中呢,前文已经提到所有类在定义时都默认继承了object,所以你猜他是啥类?
TIM截图20190308105832

2 为什么一定要加self

2.1 为什么

​ self指向的是类的实例,如下图,蓝色框中

TIM截图20190307163841

像函数一样,Python中的类方法也是一种对象。由于既可以通过实例也可以通过类来访问方法,所以在Python里有两种风格:
未绑定的类方法:没有self
通过类来引用方法返回一个未绑定方法对象。要调用它,你必须显示地提供一个实例作为第一个参数。
绑定的实例方法:有self
通过实例访问方法返回一个绑定的方法对象。Python自动地给方法绑定一个实例,所以我们调用它时不用再传一个实例参数。
两种方法都是对象,它们可以被传递、存入列表等待。两者运行时都需要一个实例作为第一参数,但当通过一个实例调用一个绑定方法时Python自动会提供一个。

​ 而在Python的编译中,如上图蓝色框中所示,t.prt()是会转化为test.prt(t),二者是等价的,所以如果在类函数定义时加入了self参数,相当于绑定了实例

​ 而如果不加self参数,那么只能够通过自己暂时实例化一个类调用,例如下图蓝框中第二种方式,如果是第一种方式,则会显示多给了一个参数,原因是编译的时候默认加入了self参数,所以显示参数多了,如红框中的使用,t4.add(1,2)=test4.add(t4,1,2)

TIM截图20190307172218

2.2 一定要self?

​ 习惯了C指针的也可以用this

TIM截图20190307172600

​ 而事实上,啥名都行,他会默认用函数的第一个参数作为self

1
2
3
4
5
6
7
class Test:
def prt(runoob):
print(runoob)
print(runoob.__class__)

t = Test()
t.prt()

2.3 是谁的self?

​ 那如果我是继承父类的函数,那么继承函数的self是谁的self呢?

​ 在继承时,是哪个实例的函数,就是谁的self,而非定义时的self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Parent:
def pprt(self):
print(self)

class Child(Parent):
def cprt(self):
print(self)
c = Child()
c.cprt()
c.pprt()
p = Parent()
p.pprt()

---输出---------------------------------------------------
<__main__.Child object at 0x0000000002A47080>
<__main__.Child object at 0x0000000002A47080>
<__main__.Parent object at 0x0000000002A47240>

3 我打(印)我自己

​ 上文提到可以访问类的各个属性,那么如果我直接打印类呢,比如 print(class) 呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
#__str__定义在类内部,必须返回一个字符串类型,
#什么时候会出发它的执行呢?打印由这个类产生的对象时,会触发执行

class People:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return '<name:%s,age:%s>' %(self.name,self.age)

p1=People('egon',18)
print(p1)
str(p1) #----->p1.__str__()

引用

Python中新式类 经典类的区别(即类是否继承object)

Python新式类与经典类的区别

python中的type和object详解

python基础——特性(property)、静态方法(staticmethod)、类方法(classmethod)、str的用法

0%