目录
2. 面向对象进阶
2.1. 成员
2.2. 变量
2.2. 方法
2.4. property属性
2.5. 成员修饰符
2.6. 对象嵌套
2.7. 特殊成员
引言
本篇内容主要是面向对象的进阶,讲解一些特殊的成员和方法,更加深入迭代器是如何通过对象实现的。
2. 面向对象进阶
2.1. 成员
面向对象中的所有成员如下:
- 变量
-
- 实例变量
- 类变量
- 方法
-
- 绑定方法
- 类方法
- 静态方法
- 属性
通过面向对象进行编程时,会遇到很多种情况,也会使用不同的成员来实现,接下来我们来逐一介绍成员特性和应用场景。
2.2. 变量
- 实例变量,属于对象,每个对象中各自维护自己的数据。
- 类变量,属于类,可以被所有对象共享,一般用于给对象提供公共数据(类似于全局变量)。
class Person(object):country = "中国"def __init__(self, name, age):self.name = nameself.age = agedef show(self):# message = "{}-{}-{}".format(Person.country, self.name, self.age)message = "{}-{}-{}".format(self.country, self.name, self.age)print(message)print(Person.country) # 中国p1 = Person("jiaoxingk",20)
print(p1.name)
print(p1.age)
print(p1.country) # 中国p1.show() # 中国-jiaoxingk-20
提示:当把每个对象中都存在的相同的示例变量时,可以选择把它放在类变量中,这样就可以避免对象中维护多个相同数据。
2.2. 方法
- 绑定方法,默认有一个self参数,由对象进行调用(此时self就等于调用方法的这个对象)【对象&类均可调用】
- 类方法,默认有一个cls参数,用类或对象都可以调用(此时cls就等于调用方法的这个类)【对象&类均可调用】
- 静态方法,无默认参数,用类和对象都可以调用。【对象&类均可调用】
class Foo(object):def __init__(self, name,age):self.name = nameself.age = agedef f1(self):print("绑定方法", self.name)@classmethoddef f2(cls):print("类方法", cls)@staticmethoddef f3():print("静态方法")# 绑定方法(对象)
obj = Foo("jiaoxingk",20)
obj.f1() # Foo.f1(obj)# 类方法
Foo.f2() # cls就是当前调用这个方法的类。(类)
obj.f2() # cls就是当前调用这个方法的对象的类。# 静态方法
Foo.f3() # 类执行执行方法(类)
obj.f3() # 对象执行执行方法
在Python中比较灵活,方法都可以通过对象和类进行调用;而在java、c#等语言中,绑定方法只能由对象调用;类方法或静态方法只能由类调用。
在类中 @classmethod 和 @staticmethod 的区别?
1 . @staticmethod不需要表示自身对象的self和自身类的cls参数(这两个参数都不需要添加),就跟使用函数一样。
2 . @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
class A:@classmethoddef test(cls): #必须有cls参数, 表示调用当前的类名print("hello")T.test()
T().test()
2.4. property属性
其实就是可以让调用属性一样去调用方法,也就是不需要加上()
属性其实是由绑定方法 + 特殊装饰器 组合创造出来的,让我们以后在调用方法时可以不加括号,例如:
class Foo(object):def __init__(self, name):self.name = namedef f1(self):return 123@propertydef f2(self):return 123obj = Foo("jiaoxingk")v1 = obj.f1()
print(v1)v2 = obj.f2
print(v2)
关于属性的编写有两种方式:
- 方式一,基于装饰器
class C(object):@propertydef x(self):pass@x.setterdef x(self, value):pass@x.deleterdef x(self):passobj = C()obj.x
obj.x = 123
del obj.x
- 方式二,基于定义变量
class C(object):def getx(self): passdef setx(self, value): passdef delx(self): passx = property(getx, setx, delx, "I'm the 'x' property.")obj = C()obj.x
obj.x = 123
del obj.x
写在最后,对property属性进行一个补充:
由于property属性和实例变量的调用方式相同,所以在编写时需要注意:属性名称 不要 实例变量 重名。
class Foo(object):def __init__(self, name, age):self.name = nameself.age = age@propertydef func(self):return 123obj = Foo("jiaoxingk", 123)
print(obj.name)
print(obj.func)
一旦重名,可能就会有报错。
class Foo(object):def __init__(self, name, age):self.name = name # 报错,错认为你想要调用 @name.setter 装饰的方法。self.age = age@propertydef name(self):return "{}-{}".format(self.name, self.age)obj = Foo("jiaoxingk", 123)
如果真的想要在名称上创建一些关系,可以让实例变量加上一个下划线。
class Foo(object):def __init__(self, name, age):self._name = nameself.age = age@propertydef name(self):return "{}-{}".format(self._name, self.age)obj = Foo("jiaoxingk", 123)
print(obj._name)
print(obj.name)
2.5. 成员修饰符
Python中成员的修饰符就是指的是:公有、私有。
- 公有,在任何地方都可以调用这个成员。
- 私有,只有在类的内部才可以调用改成员(成员是以两个下划线开头,则表示该成员为私有)。
示例一:
class Foo(object):def __init__(self, name, age):self.__name = nameself.age = agedef get_data(self):return self.__namedef get_age(self):return self.ageobj = Foo("jiaoxingk", 123)# 公有成员
print(obj.age)
v1 = self.get_age()
print(v1)# 私有成员
# print(obj.__name) # 错误,由于是私有成员,只能在类中进行使用。
v2 = obj.get_data()
print(v2)
示例2:
class Foo(object):def get_age(self):print("公有的get_age")def __get_data(self):print("私有的__get_data方法")def proxy(self):print("公有的proxy")self.__get_data()obj = Foo()
obj.get_age()obj.proxy()
特别提醒:父类中的私有成员,子类无法继承。
class Base(object):def __data(self):print("base.__data")def num(self):print("base.num")class Foo(Base):def func(self):self.num()self.__data() # # 不允许执行父类中的私有方法obj = Foo()
obj.func()
写在最后,按理说私有成员是无法被外部调用,但如果用一些特殊的语法也可以(一般不建议)。
class Foo(object):def __init__(self):self.__num = 123self.age = 19def __msg(self):print(1234)obj = Foo()
print(obj.age)
print(obj._Foo__num)
obj._Foo__msg()
成员是否可以作为独立的功能暴露给外部,让外部调用并使用。
- 可以,公有。
- 不可以,内部其他放的一个辅助,私有。
2.6. 对象嵌套
在基于面向对象进行编程时,对象之间可以存在各种各样的关系,例如:组合、关联、依赖等(Java中的称呼),通俗来说就是各种嵌套。
class Student(object):""" 学生类 """def __init__(self, name, age):self.name = nameself.age = agedef message(self):data = "我是一名学生,我叫:{},我今年{}岁".format(self.name, self.age)print(data)s1 = Student("jiaoxingk", 19)
s2 = Student("jiaoxingk", 19)
s3 = Student("日天", 19)class Classes(object):""" 班级类 """def __init__(self, title):self.title = titleself.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)item.message()c1 = Classes("五年三班")
c1.add_student(s1)
c1.add_students([s2, s3])print(c1.title)
print(c1.student_list)
可以看到,我们定义了一个班级类和学生类,我们通过班级来创建一个学生列表,其中的对象都是Student对象,相当于把Student对象嵌套进了Classes对象里面
2.7. 特殊成员
在Python的类中存在一些特殊的方法,这些方法都是 __方法__
格式,这种方法在内部均有特殊的含义,接下来我们来看一些常见的特殊成员:
__new__
,构造方法
创建空对象 , 相当于申请一个空间来建房子
class Foo(object):def __init__(self, name):print("第二步:初始化对象,在空对象中创建数据")self.name = namedef __new__(cls, *args, **kwargs):print("第一步:先创建空对象并返回")return object.__new__(cls)obj = Foo("jiaoxingk")
__init__
,初始化方法
用于进行初始化属性的 , 相当于开始装修
class Foo(object):def __init__(self, name):self.name = nameobj = Foo("jiaoxingk")
__call__
当一个类实现了__call__
方法时,你可以像调用函数一样调用该类的实例
class CallableClass:def __init__(self, name):self.name = namedef __call__(self, x):print(f"{self.name} called with argument {x}")# 创建一个CallableClass的实例
obj = CallableClass("MyCallable")# 调用实例,触发__call__方法
obj(42)
__str__
当你使用str()
函数或print()
函数来获取对象的字符串表示时,Python会调用对象的__str__
方法
class MyClass:def __init__(self, value):self.value = valuedef __str__(self):return f"MyClass instance with value: {self.value}"# 创建一个MyClass的实例
obj = MyClass(42)# 使用str()函数获取对象的字符串表示
str_representation = str(obj)# 使用print()函数打印对象的字符串表示
print(obj)
__dict__
将属性生成字典
class Foo(object):def __init__(self, name, age):self.name = nameself.age = ageobj = Foo("jiaoxingk",19)
print(obj.__dict__)# {'name': 'jiaoxingk', 'age': 19}
__getitem__
、__setitem__
、__delitem__
是Python中用于实现可索引(indexable)和可切片(slicable)对象的特殊方法。它们分别用于访问、设置和删除对象的元素,使得对象可以像列表或字典一样被操作。
class MyList:def __init__(self):self.items = []def __getitem__(self, index):return self.items[index]def __setitem__(self, index, value):self.items[index] = valuedef __delitem__(self, index):del self.items[index]def __len__(self):return len(self.items)# 创建一个MyList的实例
my_list = MyList()# 使用__setitem__添加元素
my_list[0] = 1
my_list[1] = 2
my_list[2] = 3# 使用__getitem__获取元素
print(my_list[1]) # 输出: 2# 使用__delitem__删除元素
del my_list[1]# 查看修改后的列表
print(my_list.__dict__) # 输出: {'items': [1, 3]}
__enter__
、__exit__
用于实现Python上下文管理器(Context Managers)的两个特殊方法
上下文管理器用于在代码块执行前后执行一些操作,例如资源的获取和释放,文件的打开和关闭等
class Foo(object):def __enter__(self):print("进入了")return 666def __exit__(self, exc_type, exc_val, exc_tb):print("出去了")obj = Foo()
with obj as data:print(data)
__add__
。
用于定义对象之间的加法操作
class Foo(object):def __init__(self, name):self.name = namedef __add__(self, other):return "{}-{}".format(self.name, other.name)v1 = Foo("jiaoxingk")
v2 = Foo("sb")# 对象+值,内部会去执行 对象.__add__方法,并将+后面的值当做参数传递过去。
v3 = v1 + v2
print(v3)
__iter__
-
- 迭代器
# 迭代器类型的定义:1.当类中定义了 __iter__ 和 __next__ 两个方法。2.__iter__ 方法需要返回对象本身,即:self3. __next__ 方法,返回下一个数据,如果没有数据了,则需要抛出一个StopIteration的异常。官方文档:https://docs.python.org/3/library/stdtypes.html#iterator-types# 创建 迭代器类型 :class IT(object):def __init__(self):self.counter = 0def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == 3:raise StopIteration()return self.counter# 根据类实例化创建一个迭代器对象:obj1 = IT()# v1 = obj1.__next__()# v2 = obj1.__next__()# v3 = obj1.__next__() # 抛出异常v1 = next(obj1) # obj1.__next__()print(v1)v2 = next(obj1)print(v2)v3 = next(obj1)print(v3)obj2 = IT()for item in obj2: # 首先会执行迭代器对象的__iter__方法并获取返回值,一直去反复的执行 next(对象) print(item)迭代器对象支持通过next取值,如果取值结束则自动抛出StopIteration。
for循环内部在循环时,先执行__iter__方法,获取一个迭代器对象,然后不断执行的next取值(有异常StopIteration则终止循环)。
-
- 生成器
# 创建生成器函数def func():yield 1yield 2# 创建生成器对象(内部是根据生成器类generator创建的对象),生成器类的内部也声明了:__iter__、__next__ 方法。obj1 = func()v1 = next(obj1)print(v1)v2 = next(obj1)print(v2)v3 = next(obj1)print(v3)obj2 = func()for item in obj2:print(item)如果按照迭代器的规定来看,其实生成器类也是一种特殊的迭代器类(生成器也是一个中特殊的迭代器)。
-
- 可迭代对象
# 如果一个类中有__iter__方法且返回一个迭代器对象 ;则我们称以这个类创建的对象为可迭代对象。class Foo(object):def __iter__(self):return 迭代器对象(生成器对象)obj = Foo() # obj是 可迭代对象。# 可迭代对象是可以使用for来进行循环,在循环的内部其实是先执行 __iter__ 方法,获取其迭代器对象,然后再在内部执行这个迭代器对象的next功能,逐步取值。
for item in obj:pass
class IT(object):def __init__(self):self.counter = 0def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == 3:raise StopIteration()return self.counterclass Foo(object):def __iter__(self):return IT()obj = Foo() # 可迭代对象for item in obj: # 循环可迭代对象时,内部先执行obj.__iter__并获取迭代器对象;不断地执行迭代器对象的next方法。print(item)
class Foo(object):def __iter__(self):yield 1yield 2obj = Foo()
for item in obj:print(item)