1"""
2Author: wangy325
3Date: 2024-07-18 16:28:29
4Description: 类
51) 作用域和命名空间 scope.py
62) Class对象
73) 实例对象
84) 方法对象
95) 类变量与实例变量
106) 继承
117) 多继承
128) 变量要"私有"
139) 迭代器与生成器
14"""
15
16
17class EmptyError(BaseException):
18 pass
19
20
21class MyClass:
22 # 类变量 (Java静态域)
23 property = 'MyClass\'s property'
24 trick = []
25
26 # 构造器
27 def __init__(self, *args) -> None:
28 # 实例变量(Java私有域)
29 self.data = []
30 if args is not None and len(args) > 0:
31 self.data.append(args)
32 self.size = len(self.data)
33
34 # 它既是'函数', 也是'方法对象'
35 def add(self, e):
36 self.data.append(e)
37
38 def remove(self):
39 if self.size > 0:
40 self.data.pop()
41 self.size = self.size - 1
42 else:
43 raise EmptyError('data is empty.')
44
45 def fun(self):
46 # 局部变量
47 property = 'Let\'s hava fun!'
48
49
50# type(MyClass)
51print(MyClass.__class__) # <class 'type'>
52
53# 实例对象
54cls, cls2 = MyClass(), MyClass()
55print(f'{type(cls)}')
56
57# 类变量可以在模块作用域直接读取, 并且修改
58print(MyClass.property)
59MyClass.property = f'property changed by {MyClass.__class__}'
60print(f'MyClass.property: {MyClass.property}')
61MyClass.trick.append('lie')
62
63# 实例对象也可以读取并修改类变量
64cls.property = f'property changed by {cls}'
65print(f'cls.property: {cls.property}')
66cls.trick.append('truth')
67print(f'cls.trick: {cls.trick}')
68# 注意看, cls2的property没随cls变
69# 但是 trick随cls变了
70# 这是因为, str 是'不可变常量'
71# 而list是引用对象
72print(f'cls2.property: {cls2.property}')
73print(f'cls2.trick: {cls2.trick}')
74
75# 尽管实例的修改仅仅对自己有效,
76# 但是要改动类变量是非常轻松的
77# 所以, python中的类变量是非常'不安全'的
78
79# 实例变量是隐藏在构造器中的, MyClass '看不到'它们
80try:
81 print(MyClass.data)
82except Exception as e:
83 print(f'ERROR: {e}')
84# 但是实例对象可以读取, 并修改
85cls.data.append('apple')
86print(f'All cls\'s attributes: {cls.data}, {cls.size}')
87# 同样地, 仅仅对自身有效
88print(f'All cls2\'s attributes: {cls2.data}, {cls2.size}')
89
90# 除了类变量, 模块作用域里还可以访问类里面的函数
91f = MyClass.fun
92ff = cls.fun()
93print(f'{f == ff}: {type(f)} ≠ {type(ff)}')
94# 显然, 上面2个是不一样的
95# 通过类名获取的是一个函数
96# 通过实例获取的是一个方法引用
97print(ff == MyClass.fun(cls)) # true
98
99
100# 需要说明的是, 通过实例调用的'方法',
101# 都会隐式地将该实例作为参数, 传递给方法
102# 因此, 类的方法总有一个参数: self
103
104
105# 名称改写
106# 由于python没有访问权限修饰符, 所有的变量都是`public`的
107# 为了保护'私有'变量
108# 约定将以'__'开头的变量和函数认定为私有的
109# 可以使用setter/getter方法来访问属性
110class Mapping:
111
112 def __init__(self, **args):
113 self.__dict = {}
114 self.__update(**args)
115
116 def __update(self, **kwargs):
117 self.update(**kwargs)
118
119 def update(self, **args):
120 if args is not None and len(args.keys()) > 0:
121 for k, v in args.items():
122 self.__dict[k] = v
123 # self.__dict = {k: v for k in args.keys() for v in args.values()}
124
125 def print_map(self):
126 print(self.__dict)
127
128
129mapp = Mapping(name='anna', age=18)
130mapp.print_map()
131# 名称改写, 将__dict隐藏起来了
132try:
133 print(mapp.__dict)
134except Exception as er:
135 print(er) # 'Mapping' object has no attribute '__dict'
136# 不过, 还是可以通过 _Class__attribute的方式访问
137print(mapp._Mapping__dict)
138
139
140# 方法同理
141
142# ################# #
143# Python的继承 #
144# ################# #
145
146
147class BaseClass:
148 base_class_attr = 'Base attr'
149
150 def __init__(self, arg: str):
151 print('BaseClass with-args init')
152 self.base_instance_attr = arg
153 self.__base_hidden_attr = arg
154
155 def __hidden_show(self):
156 print(f'base_class_attr: {BaseClass.base_class_attr}\n'
157 f'base_instance_attr: {self.base_instance_attr}\n'
158 f'__base_hidden_attr: {self.__base_hidden_attr}')
159
160 def show(self):
161 self.__hidden_show()
162
163
164class SubClass(BaseClass):
165 pass
166
167
168# 子类可以访问父类的类变量
169print(SubClass.base_class_attr)
170
171# 子类使用调用父类的有参构造器
172# Java是不行的
173subclass = SubClass('sub_class_attr')
174subclass.show()
175
176# ###################### #
177# 对象迭代器 #
178# ###################### #
179'''
180迭代器(iterator)
181只要是实现了 __iter__()和__next__()方法的类,
182都可以实现迭代器的行为.
183
184迭代器什么行为? 可以使用for...in
185比如list, 可以用 for e in [1, 2, 3]
186比如str, for c in 'str'
187比如file, for line in open('file.txt')
188
189在幕后, for 语句会在容器对象上调用 iter()。
190该函数返回一个定义了 __next__() 方法的迭代器对象,
191此方法将逐一访问容器中的元素。
192当元素用尽时, __next__() 将引发 StopIteration
193异常来通知终止 for 循环。
194你可以使用 next() 内置函数来调用 __next__() 方法
195
196自定义类也是如此, 可以为任何类添加迭代器
197'''
198
199
200class Iter_class():
201
202 def __init__(self, *args) -> None:
203 self.__data = list(args)
204 self.__index = len(self.__data)
205
206 def __iter__(self):
207 return self
208
209 def __next__(self):
210 if self.__index == 0:
211 raise StopIteration
212 self.__index = self.__index - 1
213 return self.__data[self.__index]
214
215 # 非必需方法
216 def has_next(self):
217 return self.__index > 0
218
219
220it = Iter_class(1, 2, 3)
221while it.has_next():
222 print(next(it), end=', ')
223for e in it:
224 print(e, end='$ ')
225
226# ############ #
227# 生成器 #
228# ############ #
229'''
230生成器用于快速创建迭代器
231
232它会自动创建__iter__()和__next__()方法
233'''
234
235
236# 以下函数创建了一个生成器
237def reverse(data):
238 for index in range(len(data) - 1, -1, -1):
239 yield data[index]
240
241
242for c in reverse('golf'):
243 print(c, end=',')
244
245# 当然, str本身就支持迭代器, 上述栗子只是为了阐述写法
246# 生成器还可以使用生成器表达式
247# 生成器表达式使用()而不是[], 后者是列表推导式!
248print()
249print(list(i for i in 'golf'))
250print(sum(i * i for i in range(10)))
251vx = [1, 2, 3]
252vy = [6, 7, 8]
253print(list(x * y for x, y in zip(vx, vy)))
254# 生成器表达式比较简单, 一般不用来出来复杂的逻辑
255# 生成器表达式相比等效的列表推导式, 更加节省内存
256l = list(e for e in range(5))
257# 等效于
258l2 = [e for e in range(5)]
259'''
260>>> (i for i in range(3))
261<generator object <genexpr> at 0x10463ba00>
262>>> [i for i in range(3)]
263[0, 1, 2]
264>>> {x:x*x for x in range(1,4,1)}
265{1: 1, 2: 4, 3: 9}
266'''
267
268# 关于yield表达式 #
269# 在生成器中使用了yield表达式,yield表达式也只在生成器中使用
270# https://docs.python.org/zh-cn/3/reference/expressions.html#yield-expressions