面向对象(二)
内置方法(一)
- 当对对象按照字典操作时,会自动触发相关方法
- 示例:
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
26class Person:
# 当对象按照字典设置键值对时,会自动触发该方法
def __setitem__(self, key, value):
# print(key, value)
self.__dict__[key] = value
# 当对象按照字典操作根据键获取值时,会自动触发该方法
def __getitem__(self, item):
print(item)
return self.__dict__[item]
# 当做字典操作,删除键值对时,会自动触发该方法
def __delitem__(self, key):
del self.__dict__[key]
p = Person()
p['name'] = 'xiaoming'
print(p.name)
# 通过字典方式添加的键值对,可以通过属性的方式获取
# print(p['name'])
# print(p.__dict__)
del p['name']
# print(p.name)
类的继承
-
相关概念:
- **继承:**父类的属性和方法子类可以直接拥有
- **派生:**子类在父类的基础上衍生出来的新的特征(属性和方法)
- **总结:**其实他们是一回事,只是描述问题的角度侧重点不同(继承描述相同点,派生侧重不同点)
-
继承:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# class Animal(object):
# 当没有指定父类时,相当于默认父类为object
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print('小动物一天到晚吃个不停')
# 继承自Animal
class Dog(Animal):
pass
d = Dog('旺财')
# 直接拥有父类的方法
d.eat()
# 直接拥有父类的属性
print(d.name) -
派生:子类可以扩充属性和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Animal:
def run(self):
print('小动物喜欢到处乱跑')
class Rabbit(Animal):
# 添加方法
def eat(self):
print('爱吃萝卜和青菜')
xiaobai = Rabbit()
xiaobai.run()
xiaobai.eat()
# 添加属性
xiaobai.color = 'white'
print(xiaobai.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
25
26class Animal:
def run(self):
print('小动物喜欢到处乱跑')
def eat(self):
print('小动物也是一天三顿')
class Cat(Animal):
# 当父类的方法完全不合适,可以覆盖重写
def run(self):
print('俺走的是猫步')
# 父类的方法合适,但是子类需要添加内容完善功能
def eat(self):
# 调用父类方法,不建议使用
# Animal.eat(self)
# super(Cat, self).eat()
# 类名及self可以不传
super().eat()
print('不过俺喜欢吃鱼')
jiafei = Cat()
jiafei.run()
jiafei.eat()
-
多继承:
- **概念:**一个类可以有多个父类
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class A:
def eat(self):
print('eat func of A')
class B:
def eat(self):
print('eat func of B')
# 多继承,多个父类使用逗号隔开
class C(B, A):
def eat(self):
# print('eat func of C')
# 当多个父类拥有相同方法时,会按照继承的先后顺序进行选择
# super().eat()
# 如果非要使用后面的类的方法时,可以明确指定进行调用
A.eat(self)
c = C()
c.eat()
访问权限
-
权限:
- **公有的:**类中的普通属性和方法,默认都是公有的,可以在类的内部、外部、子类中使用
- **私有的:**定义时在前面加两个 _,只能在本类的内部使用,不能在外部及子类中使用
-
示例:
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
46class Person:
def __init__(self, name, age):
self.name = name
# 在属性的前面添加两个'_',外部不能访问,系统内部的除外
# 默认的属性名:__age => _Person__age
self.__age = age
def eat(self):
print('民以食为天')
# 在方法的前面添加两个'_',外部不能访问,系统内部的除外
# 默认的方法名:__inner() => _Person__inner()
def __inner(self):
print('私有方法,不想让外部调用')
def test(self):
# 在类的内部可以访问私有的属性和方法
print(self.__age)
self.__inner()
p = Person('老王', 50)
# 默认所有的属性和方法都是共有的,就是在类的外面可以直接使用
p.eat()
print(p.name)
# 以下两局会报错,提示没有相关的属性或方法
# p.__inner()
# print(p.__age)
print(p.__dict__)
# 可以通过系统修改的名称找到,但是强烈建议不要这样使用
print(p._Person__age)
p._Person__inner()
p.test()
class Man(Person):
def test(self):
# print(self.__age)
# self.__inner()
print('子类无法使用父类的私有属性和方法')
m = Man('木盖', 24)
m.test()
类属性
-
说明:定义时,写在类中,但是方法外的属性,通常放在类的开头位置
-
示例:
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
30class Person:
# 定义类属性
nation = '中国'
def __init__(self, name):
self.name = name
self.nation = 'china'
# 通过类名访问类属性
print(Person.nation)
# 通过对象也可以访问类属性,但是不建议
# 当对象有同名的成员属性时,使用的就是成员属性
p = Person('xiaoming')
print(p.nation)
# 也可以动态添加
Person.hello = 'hello'
print(Person.hello)
# 特殊的类属性
# 表示类名字符串
print(Person.__name__)
# 表示父类构成的元组
print(Person.__bases__)
# 存储类相关的信息
print(Person.__dict__)
类方法
-
说明:
- 定义时使用装饰器:classmethod
- 通过类名调用
-
作用:
- 可以创建对象或者简洁的创建对象
- 对外提供简单易用的接口
-
示例1:语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Preson:
def eat(self):
print('麻辣是我的最爱')
def test(cls):
print('类方法 test')
# cls 表示当前类
print(cls)
def create(cls):
p = cls()
# 进行特定的初始化设置
p.age = 1
return p
Preson.test()
print(Preson)
# 可以创建对象或者简洁的创建对象
p = Preson.create()
print(p) -
**示例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
33
34
35
36
37
38class Number:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
def add(self):
return self.num1 + self.num2
def sub(self):
return self.num1 - self.num2
def mul(self):
return self.num1 * self.num2
def div(self):
if self.num2 == 0:
return 0
return self.num1 / self.num2
def pingfanghe(cls, num1, num2):
# 第一个数
n1 = Number(num1, num1)
# 求平方
n12 = n1.mul()
# 第二个数
n2 = Number(num2, num2)
# 求平方
n22 = n2.mul()
# 第三个数
n3 = Number(n12, n22)
# 求和
return n3.add()
print(Number.pingfanghe(3, 4))
静态方法
-
说明:
- 使用装饰器:staticmethod进行修饰
- 定义的方法没有第一个表示当前类的参数
-
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person:
def test():
print('static method test')
def create():
# 也可以创建对象
p = Person()
return p
Person.test()
p = Person.create()与类方法相比:
1
21. 除了装饰器不同,其他的基本一样,做与对象创建无关的任务时可以使用静态方法
2. 所有静态方法能够完成的功能都可以使用类方法完成
多态特性
-
定义:不同的对象,调用相同的方法有不同的响应
-
体现:多态性,不同的对象接收相同的消息会有不同的响应
-
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Animal:
def run(self):
pass
class Dog(Animal):
def run(self):
print('狗奔跑的时候一般是s型')
class Cat(Animal):
def run(self):
print('猫平时走猫步,偶尔会突然加速')
# 参数必须有run方法
def func(obj):
obj.run()
func(Dog())
func(Cat())
属性函数
-
说明:
- 使用装饰器:property 进行修饰
- 将方法当做属性一样使用
-
作用:保护特定属性,处理属性
-
示例:
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
27class User:
def __init__(self, username, password):
self.username = username
self.__password = password
# 使用property修饰的方法,可以当做属性访问
# 可以保护特定的属性
def password(self):
print('有人想直接获取密码')
return '哈哈,想偷看,没门'
# return self.__password
# 当设置password属性时,会自动调用该方法
def password(self, password):
print('注意了,有人想修改密码', password)
# 可以对密码进行特定处理,然后保存
self.__password = 'xxx' + password + 'yyy'
u = User('xiaoming', '123456')
print(u.username)
# 直接访问密码是不需要的,也是不安全的
# 直接访问password属性,自动调用使用property装饰器修饰后的password方法
print(u.password)
u.password = '654321'
内置方法(二)
-
说明:
- 把对象当做函数一样调用时,系统会自动触发
__call__
方法 - 若想支持这样的操作,需要在类中提供
__call__
方法
- 把对象当做函数一样调用时,系统会自动触发
-
示例:
1
2
3
4
5
6
7
8
9
10
11class Person:
# 把对象当做函数一样调用时,系统会自动触发该方法
def __call__(self, *args, **kwargs):
# 如计算和
return sum(args)
p = Person()
# 将对象当做函数使用,需要提供__call__方法
ret = p(1, 2, 3, 4, name='xiaoming')
print(ret)
函数判断
-
问题:如何判断一个对象是否可以像函数一样调用
-
示例:
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
32class A:
def __call__(self, *args, **kwargs):
print('xxx')
def test():
pass
# 不能使用isinstance方法判断一个对象是否是函数
# print(isinstance(test(), function))
# 打印时仍然是function类型
# print(type(test))
# 判断一个对象是否可以像函数一样调用
print(callable(test))
test()
a = A()
print(callable(a))
# a()
# 判断对象是否拥有__call__属性
print(hasattr(test, '__call__'))
print(hasattr(a, '__call__'))
# 判断是否是函数
from inspect import isfunction
print(isfunction(test))
print(isfunction(a))
数据持久化存储
-
方法:普通文件、数据库、序列化
-
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import pickle
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 存储:对象 => 文件
# xiaoming = Person('小明', 18)
# fp = open('test.txt', 'wb')
# pickle.dump(xiaoming, fp)
# print('对象数据已存储完毕')
# 读取:文件 => 对象
fp = open('test.txt', 'rb')
xiaoming = pickle.load(fp)
print(xiaoming.name)
print(xiaoming.age)
fp.close()