Python 面向对象

虽然 Python 是解释性语言,但是它是面向对象的,能够进行对象编程。

__init__() 方法

  • __init__() 方法,在创建一个对象时默认被调用,不需要手动调用
  • __init__(self) 中,默认有 1 个参数名字为 self,如果在创建对象时传递了2个实参,那么 __init__(self) 中出了self 作为第一个形参外还需要 2 个形参,例如 __init__(self,x,y)
  • __init__(self) 中的 self 参数,不需要开发者传递,python 解释器会自动把当前的对象引用传递进去
  • 所谓的self,可以理解为自己。可以把 self 当做 Java 中类里面的 this 指针一样理解,就是对象自身的意思
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义类
class Car():

# 初始化函数,用来完成一些默认的设定
# def __init__(self):
# self.color = '蓝色'
# self.wheelNum = 2

def __init__(self, wheelNum, color):
self.color = color
self.wheelNum = wheelNum

def __str__(self):
msg = "I'm a car. my color is " + self.color + ", and I hava " + str(self.wheelNum) + "个轮子."
return msg

# 创建对象
Qq = Car(4, '黄色')
print(Qq.color)
print(Qq.wheelNum)
print(Qq)

总结:

  • 在 python 中方法名如果是 __xxxx__() 的,那么就有特殊的功能,因此叫做“魔法”方法
  • 当使用 print 输出对象的时候,只要自己定义了 __str__(self) 方法,那么就会打印从在这个方法中return的数据

私有属性

Python 中没有像 Java 中 public 和 private 这样的关键字来区别公有属性和私有属性。它是以属性命名方式来区分,如果在属性名前面加了 2 个下划线 __,则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了 2 个下划线的话表示该方法是私有的,否则为公有的)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# coding=utf-8

class Person:
def __init__(self, name='xiaoming'):
self.name = name

def getName(self):
return self.name

def setName(self, name):
self.name = name

class Person2(object):
def __init__(self, name='xiahua'):
self.__name = name

def getName(self):
return self.__name

def setName(self, name):
self.__name = name

xiaobi = Person()
print(xiaobi.getName())
print(xiaobi.name)
xiaobi.name = 'xiaoci'
print(xiaobi.name)
xiaobi.setName('xiaodi')
print(xiaobi.name)

dabi = Person2()
print(dabi.__name)

继承

  • 子类在继承的时候,在定义类时,小括号 () 中为父类的名字
  • 父类的属性、方法,会被继承给子类
  • 私有的属性、方法,不会被子类继承,也不能被访问
  • 一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用
  • python 中是可以多继承的,若两个父类中有名字相同的方法,会调用前面一个的。使用 类.__mro__ 可以查看调用顺序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# coding=utf-8

# 继承
class Animal:
def run(self):
print('animal is running')

class Cat:
def __init__(self, name, color='white'):
self.name = name
self.color = color

def run(self):
print('%s -- 在跑' %self.name)

# 继承自两个类
class Bosi(Animal,Cat):
def eat(self):
print('%s -- 在吃饭' %self.name)


xiaomi = Cat('xiami')
xiaomi.run()

huihui = Bosi('huihui', 'hui')
huihui.eat()

# 两个父类中有名字相同的方法,会调用前面一个的
huihui.run()

print(Bosi.__mro__)

重写父类方法

所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。

这时若要调用在子类中父类的方法,需要使用 super()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding=utf-8
class Cat(object):
def __init__(self,name):
self.name = name
self.color = 'yellow'

class Bosi(Cat):

def __init__(self,name):
# 调用父类的__init__方法
super().__init__(name)

def getName(self):
return self.name

bosi = Bosi('xiaohua')

print(bosi.name)
print(bosi.color)

多态

定义时的类型和运行时的类型不一样,此时就成为多态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# coding=utf-8

# 多态
class F1(object):
def show(self):
print('F1.show')

class S1(F1):
def show(self):
print('S1.show')

class S2(F1):
def show(self):
print('S2.show')

def func(obj):
obj.show()

if __name__ == '__main__':
s1 = S1()
s2 = S2()

func(s1)
func(s2)

类属性和实例属性

类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,这个和 Java 中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象和实例对象访问。

如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# coding=utf-8

# 类属性
class People:
name = 'Tom' # 公有的类属性
__age = 12 # 私有类属性

p = People()

print(p.name) # 正确
print(People.name) # 正确
# print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性
# print(People.__age) #错误,不能在类外通过类对象访问私有的类属性

print('-' * 20)


# 实例属性
class People2:
address = '山东'
def __init__(self):
self.name = 'sb'
self.age = 22

p2 = People2()
print(p2.age)
p2.age = 12
print(p2.age)
print(p2.name)
print(p2.address)

print(People2.address)
# print(People2.name) # 错误,不能获取到实例属性
# print(People2.age)

p2.address = '江苏' # 会新增一个实例属性,不会影响到类属性
print(p2.address) # 实例属性会屏蔽掉同名的类属性
print(People2.address) # 类属性没有变化

People2.address = '四川'
print(p2.address) # 仍然是 江苏,因为这里引用的还是实例属性
print(People2.address)

del p2.address # 删除实例属性
print(p2.address) # 这里引用的就是类属性

类方法和静态方法

类对象所拥有的方法,需要用修饰器 @classmethod 来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以 cls 作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以 cls 作为第一个参数的名字),能够通过实例对象和类对象去访问。

需要通过修饰器 @staticmethod 来进行修饰,静态方法不需要多定义参数

从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象 cls,那么通过 cls 引用的必定是类对象的属性和方法;

而实例方法的第一个参数是实例对象 self,那么通过 self 引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高

静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# coding=utf-8

class People:
# 设置为私有属性
__address = '江苏'

# 类方法,用 classmethod 来进行修饰
@classmethod
def getAddress(cls):
return cls.__address

@classmethod
def setAddress(cls, address):
cls.__address = address

# print(People.__address)
print(People.getAddress()) # 可以通过类对象引用

p = People()
print(p.getAddress()) # 可以用过实例对象引用

p.setAddress('湖南')
print(p.getAddress())
print(People.getAddress())

class People2:
__country = 'China'

@staticmethod
def getCountry():
return People2.__country

print(People2.getCountry())

p2 = People2()
print(p2.getCountry())

__new__ 方法

  • __new__ 至少要有一个参数 cls,代表要实例化的类,此参数在实例化时由 Python 解释器自动提供

  • __new__ 必须要有返回值,返回实例化出来的实例,这点在自己实现 __new__ 时要特别注意,可以 return 父类 __new__ 出来的实例,或者直接是 object 的 __new__ 出来的实例

  • __init__ 有一个参数 self,就是这个 __new__ 返回的实例,__init____new__ 的基础上可以完成一些其它初始化的动作,__init__ 不需要返回值

  • 我们可以将类比作制造商,__new__ 方法就是前期的原材料购买环节,__init__ 方法就是在有原材料的基础上,加工,初始化商品环节

1
2
3
4
5
6
7
8
9
class A(object):
def __init__(self):
print('This is init method.')

def __new__(cls):
print('This is new method.')
return object.__new__(cls)

A()

单例模式

举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# coding=utf-8

# 创建单例-保证只有1个对象
# 实例化一个单例
class Singleton(object):
__instance = None

def __new__(cls):
# 如果 __instance 没有创建
# 那么就创建一个对象,并且赋值为这个对象的引用
# 保证下次调用这个方法时,能够知道之前已经创建过对象,这样就保证了只有一个对象
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance

a = Singleton()
b = Singleton()

print(id(a))
print(id(b))

a.age = 20

print(a.age)
print(b.age)

# 创建单例时,只执行1次__init__方法
class Singleton2(object):
__instance = None
__first_init = False

def __new__(cls, age, name):
if not cls.__instance:
cls.__instance = object.__new__(cls)

return cls.__instance

def __init__(self, age, name):
if not self.__first_init:
self.age = age
self.name = name
Singleton2.__first_init = True


a = Singleton2(18, "dongGe")
b = Singleton2(8, "dongGe")

print(id(a))
print(id(b))


print(a.age)
print(b.age)

a.age = 19
print(b.age)
hoxis wechat
一个脱离了高级趣味的程序员,关注回复1024有惊喜~
赞赏一杯咖啡
  • 本文作者: hoxis | 微信公众号【不正经程序员】
  • 本文链接: https://hoxis.github.io/python-oop.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!
  • 并保留本声明和上方二维码。感谢您的阅读和支持!
0%