面向对象进阶
概要:
- 成员
- 变量
- 实例变量
- 类变量
- 方法
- 绑定方法
- 类方法
- 静态方法
- 属性
- 变量
- 成员修饰符(公有、私有)
- ”对象嵌套“
- 特殊成员
# 1. 成员
# 1.1 变量
# 易错点&面试题
class Person:
country = "中国"
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
message = "{}-{}-{}".format(self.country, self.name, self.age)
print(message)
print(Person.country) # 中国
p1 = Person("ajax", 20)
print(p1.name) # ajax
print(p1.age) # 20
print(p1.country) # 中国
p1.show() # 中国-ajax-20
p1.name = "root" # 在对象p1中将name重置为root
p1.num = 19 # 在对象p1中新增一个实例变量 num = 19
p1.country = "china" # 在对象p1中新增实例变量 country = "china" 和类变量是没有关系的
print(p1.country) # 此时打印 china
print(Person.country) # 这里还是打印的 中国
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
class Person:
country = "中国"
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
message = "{}-{}-{}".format(self.country, self.name, self.age)
print(message)
print(Person.country) # 中国
Person.country = "大明江山"
p1 = Person("ajax", 20)
print(p1.country) # 此时输出的是 "大明江山"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
继承关系中的读写
class Base:
country = "中国"
class Person(Base):
country = "中国"
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
message = "{}-{}-{}".format(self.country, self.name, self.age)
print(message)
# 读
print(Base.country) # 中国
print(Person.country) # 中国
p1 = Person("ajax", 20)
print(p1.country) # 中国
# 写
Base.country = "china"
Person.country = "唐朝"
print(p1.country) # 唐朝
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
面试题
class Person:
x = 1
class Child1(Person):
pass
class Child2(Person):
pass
print(Person.x, Child1.x, Child2.x) # 1 1 1
Child1.x = 2
print(Person.x, Child1.x, Child2.x) # 1 2 1
Person.x = 3
print(Person.x, Child1.x, Child2.x) # 3 2 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 实例变量,属于对象,每个对象中各自维护自己的数据
- 类变量,属于类,可以被所有对象共享,一般用于给对象提供公共数据(类似于全局变量)
# 1.2 方法
- 绑定方法,默认有一个self参数,由对象进行调用(此时self就等于调用方法的这个对象)【对象、类均可调用】
- 类方法,默认有一个cls参数,用类或者对象都可以调用(此时cls就等于调用这个方法的类)【对象、类均可调用】
- 静态方法,无默认参数,用类和对象都可以调用。【对象、类均可调用】
class Foo:
def __init__(self, name):
self.name = name
def f1(self):
print("绑定方法", self.name)
@classmethod
def f2(cls):
print("类方法", cls)
@staticmethod
def f3():
print("静态方法")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
==在python中比较灵活,方法都可以通过对象和类进行调用;而在java、C#等语言中,绑定方法只能由对象调用,类方法或者静态方法只能类调用。==
面试题:
在类中@classmethod和@staticmethod的作用?
装饰上方法之后变成类方法和静态方法,一个有cls参数,一个没有参数;根据你的方法内部实现的功能,如果需要cls,就选择类方法,如果没有参数,就选择静态方法。
# 1.3 属性
属性是由绑定方法 + 特殊装饰器 组合创造出来的。让我们以后在调用方法时可以不加括号。
class Foo:
def __init__(self, name):
self.name = name
def f1(self):
return 123
@property
def f2(self):
return 123
obj = Foo("wujie")
obj.f1()
obj.f2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
案例:
class Pagination:
def __init__(self, current_page, per_page_num=10):
self.per_page_num = per_page_num
if not current_page.isdecimal():
self.current_page = 1
return
current_page = int(current_page)
if current_page < 1:
self.current_page = 1
return
self.current_page = current_page
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
user_list = ["用户-{}".format(i) for i in range(1, 3000)]
# 面向对象的思路
while True:
page = input("请输入页码")
pg_object = Pagination(page, 10)
pg_data_list = user_list[pg_object.start:pg_object.end]
for item in pg_data_list:
print(item)
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
关于属性的编写有两种方式:
方式一,基于装饰器(
@property
)基于定义变量
class C: @x.setter def x(self, value): pass @x.deleter def x(self): pass def getx(self): pass def setx(self, value): pass def delx(self): pass y = property(getx, setx, delx, "I'm the 'x' property.") obj = C() obj.x = 123 # 赋值操作自动触发 setter 操作 del obj.x # 删除操作自动触发 deleter 操作
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补充:
由于属性和实例变量的调用方式一样,所以在编写时需要注意:属性名称 不要和实例变量重名
==一旦重名,就会报错==
# 2. 成员修饰符
python中成员的修饰符就是指的是:公有,私有
- 公有:在任何地方都可以调用这个成员
- 私有:只有在类的内部才可以调用改成员(成员是以两个下划线开头,则表示该成员为私有)
示例一
class Foo: def __init__(self, name, age): self.__name = name self.age = age def get_data(self): return self.__name obj = Foo()obj.age# obj.__name 无法访问obj.get_data() # 通过方法来调用私有成员
成员是否可以作为独立的功能暴露给外部,让外部调用并使用
- 可以,公有
- 不可以,内部其他方法的一个辅助,私有
# 3. 对象嵌套
在基于面向对象编程时,对象之间可以存在各种各样的关系:例如,组合,依赖等(java中的称呼)
用白话来说就是各种嵌套。
情景一:
class Student: """学生类""" def __init__(self, name, age): self.name = name self.age = age def message(self): message = "{}-{}".format(self.name, self.age) print(message)class Classes: """班级类""" def __init__(self, title): self.title = title self.student_list = [] def add_student(self, stu_object): self.student_list.append(stu_object) def add_students(self, stu_object_list): for stu in stu_object_list: self.add_student(stu) def show_members(self): for item in self.student_list: print(item)s1 = Student("wujie", 20)s2 = Student("alex", 34)s3 = Student("ritian", 19)c1 = Classes("三年二班")c1.add_student(s1)c1.add_students([s2, s3])
情景二:
class Student: def __init__(self, name, age, class_object): self.name = name self.age = age self.class_object = class_object def message(self): message = "{}-{}-{}".format(self.class_object.title, self.name, self.age) print(message)class Classes: def __init__(self, title): self.title = titlec1 = Classes("python全栈")c2 = Classes("Linux云计算")user_object_list = [ Student("wujie", 20, c1), Student("alex", 34, c1), Student("ritian", 19, c2)]for obj in user_object_list: print(obj.name, obj.age, obj.class_object.title)
情景三:
class Student: def __init__(self, name, age, class_object): self.name = name self.age = age self.class_object = class_object def message(self): message = "{}-{}-{}".format(self.class_object.title, self.name, self.age) print(message)class Classes: def __init__(self, title, school_object): self.title = title self.school_object = school_objectclass School: def __init__(self, name): self.name = names1 = School("北京校区")s2 = School("上海校区")c1 = Classes("python全栈", s1)c2 = Classes("Linux云计算", s2)user_object_list = [ Student("wujie", 20, c1), Student("alex", 34, c1), Student("ritian", 19, c2)]for obj in user_object_list: print(obj.name, obj.age, obj.class_object.title, obj.class_object.school_object.name)
# 4. 特殊成员
在python的类中存在一些特殊的方法,这些方法都是__方法__
的格式,这种方法在内部均有特殊的含义。
__init__
初始化方法class Foo(object): def __init__(self, name): self.name = name obj = Foo("wujie")
1__new__
构造方法class Foo: def __init__(self, name): print("第二步,初始化对象,在空对象中创建数据") self.name = name def __new__(cls, *args, **kwargs): print("第一步:先创建空对象并返回") return object.__new__(cls) obj = Foo("wujie")
1__call__
class Foo(object): def __call__(self, *args, **kwargs): print("执行call方法") obj = Foo()obj()
1__str__
class Foo: def __str__(self): return "哈哈哈哈" obj = Foo()data = str(obj) # 可以用于类里格式化输出对象print(data)
1__dict__
class Foo: def __init__(self, name, age): self.name = name self.age = age obj = Foo("wujie", 19)print(obj.__dict__) # 自动获取对象、类内部实例变量,并自动转换为字典格式
1__getitem__
__setitem__
___delitem__
class Foo: def __getitem__(self, itme): pass def __setitem__(self, key, value): pass def __delitem__(self, key): pass obj = Foo()obj["xxx"] = 123 # 自动触发类中 __setitem__方法print(obj["xxx"]) # 自动触发类中 __getitem__ 方法 中括号内的值当做参数传递给它del obj["xxx"] # 自动触发类中 __delitem__ 方法,[]里的值当做参数传递给它
1__enter__
__exit__
class Foo: def __enter__(self): print("进来了") return 666 # 对应的 as 后面的 f 这里返回什么,f就是什么 def __exit__(self, exc_type, exc_val, exc_tb): print("出去了")obj = Foo()# with 对象 在内部会执行它的__enter__方法,当with缩进中的代码执行完了,自动执行__exit__方法with obj as f: print(123) print(f)
1超前知识:数据连接,每次对远程的数据进行操作时,都必须经历。1. 连接 = 连接数据库2. 操作数据库3. 关闭连接
1# 伪代码class SqlHelper: def __enter__(self): self.连接 = 连接数据库 return 连接 def __exit__(self, exc_type, exc_val ,exc_tb): self.连接.关闭 with SqlHelper() as 连接: 连接.操作...
1# 面试题class Context: def __enter__(self): # 返回的内容 考核点 return self def __exit__(self, exc_type, exc_val, exc_tb): pass @staticmethod def do_something(): print("内部执行")with Context() as ctx: print("内部执行") ctx.do_something()
1一般称之为 上下文管理的语法。
__add__
等class Foo: def __init__(self, name): self.name = name def __add__(self, other): return self.name + other.namev1 = Foo("alex")v2 = Foo("烧饼")# 对象 + 值, 内部会执行 对象.__add__方法,会将后面的值当做参数传递进去,v3 = v1 + v2print(v3)
1__iter__
迭代器
# 迭代器的含义:1. 当类中定义了__iter__ 和__next__ 两个方法2. __iter__ 方法需要返回对象本身 即 self3. __next__ 方法,返回下一个数据,如果没有数据了,则需要抛出一个StopIteration的异常# 创建迭代器类型class IT: def __init__(self): self.counter = 0 def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == 3: raise StopIteration() return self.counter # 根据类实例化一个迭代器对象obj1 = IT()# 内置函数 nextv1 = next(obj1) # obj1.__next__()print(v1)迭代器对象支持通过next取值,如果取值结束就自动跳出StopIterationfor循环内部在循环时,先执行__iter__方法,获取一个迭代器对象,然后不断地执行next取值,有StopIteration则停止循环。
1生成器
# 创建生成器函数def func() yield 1 yield 2 # 创建生成器对象,(内部是根据生成器类generator创建的对象),生成器类的内部也声明了__iter__,__next__方法,也支持for循环# 如果按照迭代器的规定来看,其实生成器类也是一种特殊的迭代器类(生成器也是一个特殊的迭代器)
1可迭代对象
# 如果一个类中定义了一个__iter__方法且返回了一个迭代器对象,则我们可以称这个类创建的对象为可迭代对象class Foo: def __iter__(self): return 迭代器对象(或生成器对象) obj = Foo() # obj是可迭代对象,不管返回什么,都叫可迭代对象# 那这个对象将可以被for循环,内部先执行__iter__方法,获取迭代器对象,然后在在内部调用__next__方法,逐步取值。
1# 基于可迭代对象&迭代器实现:自定义rangeclass IterRange(object): def __init__(self, num): self.num = num self.counter = -1 def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == self.num: raise StopIteration() return self.counterclass XRange(object): def __init__(self, max_num): self.max_num = max_num def __iter__(self): return IterRange(self.max_num)obj = XRange(10)for item in obj: print(item)
1class Foo: def __iter__(self): yield 1 yield 2 obj = Foo()for item in obj: print(item)
1# 基于可迭代对象&生成器,实现自定义rangeclass Xrange: def __init__(self, max_num): self.max_num = max_num def __iter__(self): counter = 0 while counter < self.max_num: yield counter counter += 1 obj = Xrange(100)for item in obj: print(item)
1常见的数据类型:
v1 = list([11, 22, 33,44])v1 是一个课迭代对象,因为在列表中声明了一个__iter__方法并且返回一个迭代器对象
1代码判断是可迭代对象还是迭代器
from collections.abc import Iterator, Iterablev1 = [11, 22, 33]print(isinstance(v1, Interator)) # False 判断依据是__iter__和__next__v2 = v1.__iter__()print(isinstance(v2, Iterator)) # True 是迭代器对象v1 = [11, 22, 33]print(isinstance(v1, Iterable)) # True 判断依据:是否有__iter__方法且返回迭代器对象 ,说明是一个可迭代对象
1
# 总结
- 面向对象编程中的成员
- 变量
- 实例变量
- 类变量
- 方法
- 绑定方法
- 类方法
- 静态方法
- 属性
- 变量
- 成员修饰符
- 对象中的数据嵌套
- 特殊成员
- 重要概念:
- 迭代器
- 生成器
- 可迭代对象
# 作业
列举面向对象的成员并简述他们的特点。
@staticmethod 和 @classmethod的作用是什么?静态方法和类方法,静态方法没有参数,类方法有cls参数代表类本身
面向对象中如何让成员变为私有。
__
加两个下划线__new__
方法的作用?构造方法为类创造一个空的对象,在__init__
方法之前执行简述你理解的:迭代器、生成器、可迭代对象。
迭代器:含有
__iter__
、__next__
方法,前者返回自身,后者取下一个数据,终止是抛出StopIteration,也可以进行for循环生成器:指函数内含有
yield
关键字的都称之为生成器函数,执行函数获得生成器对象 (一种特殊的迭代器),可以通过next取值可迭代对象:含有
__iter__
方法的,且返回一个迭代器对象,可以被for循环看代码写结果
class Foo(object): a1 = 1 def __init__(self,num): self.num = num def show_data(self): print(self.num+self.a1) obj1 = Foo(666)obj2 = Foo(999)print(obj1.num)print(obj1.a1)obj1.num = 18obj1.a1 = 99print(obj1.num)print(obj1.a1)print(obj2.a1)print(obj2.num)print(obj2.num)print(Foo.a1)print(obj1.a1)
1看代码写结果,注意返回值。
class Foo(object): def f1(self): return 999 def f2(self): v = self.f1() print('f2') return v def f3(self): print('f3') return self.f2() def run(self): result = self.f3() print(result)obj = Foo()v1 = obj.run()print(v1)# f3# f2# 999# None
1看代码写结果【如果有错误,则标注错误即可,并且假设程序报错可以继续执行】
class Foo(object): def f1(self): print('f1') @staticmethod def f2(): print('f2') obj = Foo()obj.f1() # f1obj.f2() # f2Foo.f1() # 会报错,需要传递 obj Foo.f2() # f2
1看代码写结果【如果有错误,则标注错误即可,并且假设程序报错可以继续执行】
class Foo(object): def f1(self): print('f1') self.f2() self.f3() @classmethod def f2(cls): print('f2') @staticmethod def f3(): print('f3')obj = Foo()obj.f1()# f1# f2# f3
1看代码写结果【如果有错误,则标注错误即可,并且假设程序报错可以继续执行】
class Base(object): @classmethod def f2(cls): print('f2') @staticmethod def f3(): print('f3')class Foo(Base): def f1(self): print('f1') self.f2() self.f3()obj = Foo()obj.f1()# f1# f2# f3
1看代码写结果【如果有错误,则标注错误即可,并且假设程序报错可以继续执行】
class Foo(object): a1 = 1 __a2 = 2 def __init__(self,num): self.num = num self.__salary = 1000 def show_data(self): print(self.num+self.a1) obj = Foo(666)print(obj.num) # 666print(obj.a1) # 1print(obj.__salary) # 报错,取不到print(obj.__a2) # 报错,取不到print(Foo.a1) # 1print(Foo.__a2) # 报错,取不到,私有的obj.show_data() # 667
1看代码写结果
class Foo(object): def __init__(self, age): self.age = age def display(self): print(self.age) # 没有返回结果data_list = [Foo(8), Foo(9)] # 两个对象# print(data_list[0].age)# data_list[1].display()for item in data_list: print(item.age, item.display())# 输出结果88 None99 None
1看代码写结果
class Base(object): def __init__(self, a1): self.a1 = a1 def f2(self, arg): print(self.a1, arg)class Foo(Base): def f2(self, arg): print('666')obj_list = [Base(1), Foo(2), Foo(3)]for item in obj_list: item.f2(1) # 输出1 1666666
1看代码写结果
class Foo(object): def __init__(self, num): self.num = num v1 = [Foo for i in range(10)]v2 = [Foo(5) for i in range(10)]v3 = [Foo(i) for i in range(10)]print(v1) # 10个类,类名print(v2) # 10个类对象,每次传了num=5print(v3) # 10个类对象里传入0-9的参数
1看代码写结果
class StarkConfig(object): def __init__(self, num): self.num = num def changelist(self, request): print(self.num, request)config_obj_list = [ StarkConfig(1), StarkConfig(2), StarkConfig(3) ]for item in config_obj_list: print(item.num) # 输出123
1看代码写结果
class StarkConfig(object): def __init__(self, num): self.num = num def changelist(self, request): print(self.num, request)config_obj_list = [StarkConfig(1), StarkConfig(2), StarkConfig(3)]for item in config_obj_list: item.changelist(666) # 输出1 6662 6663 666
1看代码写结果
class StarkConfig(object): def __init__(self, num): self.num = num def changelist(self, request): print(self.num, request) def run(self): self.changelist(999)class RoleConfig(StarkConfig): def changelist(self, request): print(666, self.num)class AdminSite(object): def __init__(self): self._registry = {} def register(self, k, v): self._registry[k] = vsite = AdminSite()site.register('武沛齐', StarkConfig(19))site.register('root', StarkConfig(20))site.register("admin", RoleConfig(33))print(len(site._registry)) # 3for k, row in site._registry.items(): row.changelist(5)# 输出19 520 5666 33
1看代码写结果(如有报错,请标注报错位置)
class StarkConfig(object): def __init__(self, num): self.num = num def run(self): self() def __call__(self, *args, **kwargs): print(self.num) class RoleConfig(StarkConfig): def __call__(self, *args, **kwargs): print(345) def __getitem__(self, item): return self.num[item] v1 = RoleConfig('alex')v2 = StarkConfig("wupeiqi")print(v1[1]) # lprint(v2[2]) # 报错了
1补全代码
class Context: @staticmethod def do_something(): passwith Context() as ctx: ctx.do_something()
1看代码写结果
class Department(object): def __init__(self,title): self.title = titleclass Person(object): def __init__(self,name,age,depart): self.name = name self.age = age self.depart = depart def message(self): msg = "我是%s,年龄%s,属于%s" %(self.name,self.age,self.depart.title) print(msg) d1 = Department('人事部')d2 = Department('销售部')p1 = Person('武沛齐',18,d1)p2 = Person('alex',18,d1)p1.message() # 武沛齐 18 人事部p2.message() # alex 18 人事部
1分析代码关系,并写出正确的输出结果。
class Node(object): def __init__(self, title): self.title = title self.children = [] def add(self, node): self.children.append(node) def __getitem__(self, item): return self.children[item]root = Node("中国")root.add(Node("河南省"))root.add(Node("河北省"))print(root.title) # 中国print(root[0]) # 对象print(root[0].title) # 河南省print(root[1]) # 第二个对象print(root[1].title) # 河北省
1分析代码关系,并写出正确的输出结果。
class Node(object): def __init__(self, title): self.title = title self.children = [] def add(self, node): self.children.append(node) def __getitem__(self, item): return self.children[item]root = Node("中国")root.add(Node("河南省"))root.add(Node("河北省"))root.add(Node("陕西省"))root.add(Node("山东省"))root[1].add(Node("石家庄"))root[1].add(Node("保定"))root[1].add(Node("廊坊"))root[3].add(Node("潍坊"))root[3].add(Node("烟台"))root[3].add(Node("威海"))root[1][1].add(Node("雄安"))root[1][1].add(Node("望都"))print(root.title) # 中国print(root[0].title) # 河南省print(root[1].title) # 河北省print(root[1][0].title) # 石家庄print(root[1][2].title) # 廊坊print(root[1][1][0].title) # 雄安
1