Python2
魔法方法
-
__init__()
(1)
__init__()
方法:构造器,当一个实例被创建的时候调用的初始化方法。 -
__new__()
(1)
__new__(cls, [...])
是在一个对象实例化的时候所调用的第一个方法,所以它才是真正意义上的构造方法。
(2)它的第一个参数是这个类,其他的参数是用来直接传递给__init__
方法。 -
__del__()
类的表示
__str__() / __repr__()
这两个方法都是用来描述类或对象信息的,比如你直接实例化了一个对象,打印出来的是这个对象的地址。而要是重新在类中定义了这两个方法,那打印对象的结果就是方法返回的信息。
__repr__
通常情况下,直接输出某个实例化对象,本意往往是想了解该对象的基本信息,例如该对象有哪些属性,它们的值各是多少等等。但默认情况下,我们得到的信息只会是“类名+object at+内存地址”,对我们了解该实例化对象帮助不大。
默认情况下,repr() 会返回和调用者有关的 “类名+object at+内存地址”信息。当然,我们还可以通过在类中重写这个方法,从而实现当输出实例化对象时,输出我们想要的信息。
class CLanguage:
def __init__(self):
self.name = "C语言中文网"
self.add = "http://c.biancheng.net"
def __repr__(self):
return "CLanguage[name="+ self.name +",add=" + self.add +"]"
clangs = CLanguage()
print(clangs)
__str__
如果要把一个类的实例变成 str,就需要实现特殊方法__str__():
class Student(object):
def __init__(self,id,name,age):
self.id=id
self.name=name
self.age=age
def __str__(self):
return "学号:{}--姓名:{}--年龄{}".format(self.id,self.name,self.age)
s=Student(111,"Bob",18)
print(s)
__bool__()
当调用 bool(obj) 时,会调用 __bool__()
方法,返回 True 或 False:
class Person(object):
def __init__(self, uid):
self.uid = uid
def __bool__(self):
return self.uid > 10
p1 = Person(4)
p2 = Person(14)
print(bool(p1)) # False
print(bool(p2)) # True
__eq__()
__ne__()
__lt__()
__gt__()
比较大小用的 和上面的一样
访问控制
__setattr__
:定义当一个属性被设置时的行为
__getattr__
:定义当用户试图获取一个不存在的属性时的行为
__delattr__
:删除某个属性时调用
__getattribute__
:访问任意属性或方法时调用
7、容器类操作(重要)
__setitem__
(self, key, value):定义设置容器中指定元素的行为,相当于 self[key] = value;
__getitem__(self, key)
: 定义获取容器中指定元素的行为,相当于 self[key];
__delitem__
(self, key):定义删除容器中指定元素的行为,相当于 del self[key];
__len__
(self):定义当被 len() 调用时的行为(返回容器中元素的个数);
__iter__
(self):定义当迭代容器中的元素的行为;
__contains__
(self, item):定义当使用成员测试运算符(in 或 not in)时的行为;
__reversed__
(self):定义当被 reversed() 调用时的行为。
Python 中常见的容器类型有:
- 字典
- 元组
- 列表
- 字符串
因为它们都是「可迭代」的。可迭代是因为,它们都实现了容器协议
class MyList(object):
"""自己实现一个list"""
def __init__(self, values=None):
# 初始化自定义list
self.values = values or []
self._index = 0
def __setitem__(self, key, value):
# 添加元素
self.values[key] = value
def __getitem__(self, key):
# 获取元素
return self.values[key]
def __delitem__(self, key):
# 删除元素
del self.values[key]
def __len__(self):
# 自定义list的元素个数
return len(self.values)
def __iter__(self):
# 可迭代
return self
def __next__(self):
# 迭代的具体细节
# 如果__iter__返回self 则必须实现此方法
if self._index >= len(self.values):
raise StopIteration()
value = self.values[self._index]
self._index += 1
return value
def __contains__(self, key):
# 元素是否在自定义list中
return key in self.values
def __reversed__(self):
# 反转
return list(reversed(self.values))
# 初始化自定义list
my_list = MyList([1, 2, 3, 4, 5])
print(my_list[0]) # __getitem__
my_list[1] = 20 # __setitem__
print(1 in my_list) # __contains__
print(len(my_list)) # __len__
print([i for i in my_list]) # __iter__
del my_list[0] # __del__
reversed_list = reversed(my_list) # __reversed__
print([i for i in reversed_list]) # __iter__
这个例子实现了一个 MyList 类,在这个类中,定义了很多容器类的魔法方法。这样一来,这个 MyList 类就可以像操作普通 list 一样,通过切片的方式添加、获取、删除、迭代元素了。
__call__(self, [args...])
序列化
Python 提供了序列化模块 pickle
,当使用这个模块序列化一个实例化对象时,也可以通过魔法方法来实现自己的逻辑,这些魔法方法包括:
__getstate__()
__setstate__()
pickle.dumps()
是 Python 中的一个函数,用于将 Python 对象序列化为一个字节流,以便将其保存到文件、传输到网络或在进程之间进行通信。
以下是一个使用 pickle.dumps()
的示例:
import pickle
data = {'name': 'Alice', 'age': 25, 'is_student': True}
# 将数据序列化为字节流
bytes_data = pickle.dumps(data)
# 输出字节流长度和内容
print(len(bytes_data))
print(bytes_data)
output:
b'\x80\x04\x95*\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x05Alice\x94\x8c\x03age\x94K\x19\x8c\nis_student\x94\x88u.'
在这个例子中,我们先定义了一个包含三个键值对的字典
data
。然后,我们使用pickle.dumps()
函数将该字典序列化为一个字节流,并将结果存储在变量bytes_data
中。最后,我们输出了字节流的长度和内容。需要注意的是,使用
pickle
序列化和反序列化对象时,必须保证两者使用相同的协议和版本。否则可能会导致不兼容的问题。此外,在序列化敏感信息(例如密码)等时应格外小心,以免泄漏敏感信息。
import pickle
class Person(object):
def __init__(self, name, age, birthday):
self.name = name
self.age = age
self.birthday = birthday
def __getstate__(self):
# 执行 pick.dumps 时 忽略 age 属性
return {
'name': self.name,
'birthday': self.birthday
}
def __setstate__(self, state):
# 执行 pick.loads 时 忽略 age 属性
self.name = state['name']
self.birthday = state['birthday']
person = Person('李四', 20, (2017, 2, 23))
pickled_person = pickle.dumps(person) # 自动执行 __getstate__ 方法
p = pickle.loads(pickled_person) # 自动执行 __setstate__ 方法
print(p.name, p.birthday) # 李四 (2017, 2, 23)
# 由于执行 pick.loads 时 忽略 age 属性,所以下面执行回报错
print(p.age) # AttributeError: 'Person' object has no attribute 'age'
面向对象
继承
子类如果重写了
__init__
方法,子类就不会自动继承父类__init__
中的属性。如果要继承父类的属性,需要用到super方法,我们在B类的__init__
方法中加上:
super(子类,self).__init__(参数0,参数1...)
父类名称.__init__(self,参数0,参数1...)
复制代码
若继承父类的所有属性就直接用super().__init__()
。
- 在调用基类方法时,需加上基类的类名前缀,并带上self参数变量。但在类中调用普通函数不需要带上self函数。
多态和封装略
面向对象的各种方法
1、静态方法 (用这个装饰器来表示 @staticmethod
)
@staticmethod
下面的函数就不属于这个类了,只不是还是要通过类名的方式调用 .变成了类方法
@staticmethod
定义的静态方法不需要访问实例和类属性,因此其参数列表中不需要包含 self
或 cls
参数。而 @classmethod
定义的类方法必须接受一个 cls
参数作为第一个参数,该参数指代调用该方法的类本身。
因为self.name这个变量是实例化这个类传进去的,类方法是不能访问实例变量的,只能访问类里面定义的变量
正确示例:
class Person(object):
def __init__(self, name):
self.name = name
@staticmethod # 把eat方法变为静态方法
def eat(x):
print("%s is eating" % x)
d = Person("xiaoming")
d.eat("jack")
Person.eat("jack")
#就把eat方法当作一个独立的函数给他传参就行了 不能传入name
2.类方法@classmethod
类方法只能访问类变量,不能访问实例变量
错误示例:
class Person(object):
def __init__(self, name):
self.name = name
@classmethod # 把eat方法变为类方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat()
###########
结果:
AttributeError: type object 'Person' has no attribute 'name'
因为self.name这个变量是实例化这个类传进去的,类方法是不能访问实例变量的,只能访问类里面定义的变量
正确的如下
class Person(object):
name="杰克"
def __init__(self, name):
self.name = name
@classmethod # 把eat方法变为类方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat()
-
定义在类中的变量叫类变量、方法之外的变量。与实例变量不同,类变量被所有实例共享,并且可以通过类名或实例名来访问。
-
实例变量在 Python 中,实例变量是定义在类的方法中,以 self. 开头的变量。与类变量不同,实例变量只能在实例内部访问和修改,并且每个实例都有自己的一组实例变量。
以下是一个使用实例变量的示例:
在这个例子中,我们定义了一个名为
MyClass
的类,并在类的构造方法中定义了两个实例变量x
和y
。我们还定义了一个名为add_xy()
的实例方法,该方法将两个实例变量相加并返回结果。在主程序中,我们创建了两个MyClass
的实例obj1
和obj2
,并分别调用它们的add_xy()
方法。由于obj1
的x
和y
分别是3
和5
,因此其add_xy()
方法返回的结果是8
。而obj2
的x
和y
分别是7
和9
,因此其add_xy()
方法返回的结果是16
。最后,我们通过修改实例变量的值来测试add_xy()
方法是否正确地计算了结果。class MyClass: def __init__(self, x, y): self.x = x self.y = y def add_xy(self): return self.x + self.y obj1 = MyClass(3, 5) obj2 = MyClass(7, 9) print(obj1.add_xy()) # 输出 8 print(obj2.add_xy()) # 输出 16
3、属性方法 (用这个装饰器表示 @property)
把一个方法变成一个静态属性,属性就不用加小括号那样的去调用了
因为eat此时已经变成一个属性了, 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了
class Person(object):
def __init__(self, name):
self.name = name
@property # 把eat方法变为属性方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat