Python/类
概述
编辑 class ClassName(Base1, Base2, Base3):
"""Class documentation"""
def __init__(self,參數):
語句
def funcName(self,參數): #self 不是 python 關鍵字,把它換成其它名字也是表示類實例
語句
localVar = value0
self.DataMember = value1 # instance attribute
self.__privateDataMember2 = value2 # instance private attribute
dataMember1 = value # A class attribute, not an instance one
<statement-N>
数据成员可分为
- 类的变量(类的属性 class attribute):类定义内部、但在成员函数之外 赋值的变量。可以按照 className.VarName或者instanceName.VarName读写,但是用instanceName.VarName写入值的时候,实际上是自动作为实例的变量写值;实例变量会屏蔽对同名类变量的访问。
- 实例的变量(实例的属性 instance attribute):类的成员函数内部用self.VarName=Value 这种方式赋值的变量。不能被其它实例读写
- 本地变量:类的成员函数内部用VarName=Value 这种方式赋值的变量。尽在当前函数内部有效。
方法成员可分为:
- 实例方法:一般第一个参数是self
- 类方法:通常将第一个参数命名为cls。方法调用时会自动把类本身绑定给 cls 参数。类方法需要使用@classmethod修饰符进行修饰。类方法推荐使用类名直接调用。classmethod 主要用于操作类级别的数据或创建类的实例。它可以访问和修改类的属性,而不能直接访问实例的属性。
- 静态方法:使用@staticmethod修饰。没有隐式的第一个参数(如 self 或 cls)。它不访问类的属性或实例的属性。静态方法更像是一个普通的函数,只是定义在类的命名空间下。不能访问类或实例的属性或方法。通过类或实例调用 staticmethod。
在类的方法之外的代码,会在类定义时直接执行。例如一行print语句,就会直接屏幕输出。
封装功能是把一些数据与方法限于类的内部使用,不对外暴露。Python通过在变量名字与方法名字前加入2个下划线实现。
类的内部实现
编辑类的字典(Class Dictionar)用vars(ClassName)或ClassName.__dict__访问,其中包含了:
- 类的变量名与值
- 类的方法名与地址
- __module__与对应值
- __dict__
- __weakref__
- __doc__与字符串值
实例的字典用用vars(InstanceName)或InstanceName.__dict__访问,其中包含了实例变量的名字与值。
在外部访问一个不存在的实例属性,这自动增加这个实例属性。用del关键字可以删除实例属性。这叫做动态类结构(Dynamic Class Structure)
类的继承
编辑如果子类方法遮蔽了父类的同名方法,在子类中调用父类的被遮蔽的方法的方式有2种,分别是:
- 类可以看做一个独立空间,在类的外部调用其中的实例方法,可以向调用普通函数那样,只不过需要额外备注类名(此方式又称为未绑定方法);
- super() 函數用於調用父類实例的方法。但如果涉及多继承,该函数只能调用第一个直接父类实例的方法。
如果在子类中定义构造方法,则必须在该方法中调用父类的构造方法。
class People:
def __init__(self,name):
self.name = name
def say(self):
print("我是人,名字为:",self.name)
class Animal:
def __init__(self,food):
self.food = food
def display(self):
print("我是动物,我吃",self.food)
class Person(People, Animal):
#自定义构造方法
def __init__(self,name,food):
#调用 People 类的构造方法
super().__init__(name)
#super(Person,self).__init__(name) #执行效果和上一行相同
#People.__init__(self,name)#使用未绑定方法调用 People 类构造方法
#调用其它父类的构造方法,需手动给 self 传值
Animal.__init__(self,food)
per = Person("zhangsan","熟食")
per.say()
per.display()
类的特殊方法
编辑魔法方法(Magic Methods),有时也叫做特殊方法(Special Methods)、双下方法(Dunder Methods),是一些以双下划线(__)开始和结束的方法。这些方法通常是由 Python 解释器在特定情况下自动调用的,用于实现对象的特定行为和操作。魔法方法的设计目的是使得你可以自定义类的行为,以便让它们能够像内建类型一样工作。例如,定义对象加法、比较、索引、迭代等操作。
- __init__ : 初始化函数,在生成对象时调用
- __del__ : 析构函数,释放对象时使用
- __repr__ : 直接输入instanceName再回车时输出一个字符串。
- __str__: print(instanceName)时输出一个字符串。
- __len__: 获得长度
- __enter__ 与 __exit__ :专用于with语句
- __new__:Metaclass构造函数
函数 | 间接形式 | 直接形式 |
---|---|---|
__getattr__ | getattr(A, B) | A.B |
__setattr__ | setattr(A, B, C) | A.B = C |
__delattr__ | delattr(A, B) | del A.B |
操作符重载,各个操作数必须都是该类的实例。增强赋值操作符(如 +=)时,Python 会先尝试调用 就地运算符方法,如 __iadd__。如果没有定义该方法,Python 会退回到调用普通的双元运算符方法,例如 __add__。这些方法返回一个新的对象,并将其重新赋值给左侧的变量。 __iadd__中的字母 i 代表 "in-place",即 "就地" 的意思。
函数 | 运算符 |
---|---|
__add__ | A + B |
__sub__ | A - B |
__mul__ | A * B |
__truediv__ | A / B |
__floordiv__ | A // B |
__mod__ | A % B |
__pow__ | A ** B |
__and__ | A & B |
__or__ | A | B |
__xor__ | A ^ B |
__eq__ | A == B |
__ne__ | A != B |
__gt__ | A > B |
__lt__ | A < B |
__ge__ | A >= B |
__le__ | A <= B |
__lshift__ | A << B |
__rshift__ | A >> B |
__contains__ | A in B A not in B |
函数 | 运算符 |
---|---|
__pos__ | +A |
__neg__ | -A |
__inv__ | ~A |
__abs__ | abs(A) |
__len__ | len(A) |
函数 | 运算符 |
---|---|
__getitem__ | C[i] |
__setitem__ | C[i] = v |
__delitem__ | del C[i] |
__getslice__ | C[s:e] |
__setslice__ | C[s:e] = v |
__delslice__ | del C[s:e] |
函数 | 运算符 |
---|---|
__cmp__ | cmp(x, y) Python3弃用 |
__hash__ | hash(x) |
__nonzero__ | bool(x) |
__call__ | instanceName(params) |
__iter__ | iter(x) |
__reversed__ | reversed(x) 逆序迭代器 |
__divmod__ | divmod(x, y) |
__int__ | int(x) |
__long__ | long(x) |
__float__ | float(x) |
__complex__ | complex(x) |
__hex__ | hex(x) |
__oct__ | oct(x) |
__index__ | 在需要整数的场景中返回一个整数值,可用于bin()、oct()、hex()、位运算符如 <<(左移)和 >>(右移)、作为range() 函数的参数、作为list、元组、字符串等的下标 |
__copy__ | copy.copy(x) |
__deepcopy__ | copy.deepcopy(x) |
__sizeof__ | sys.getsizeof(x) (2.6+) |
__trunc__ | math.trunc(x) (2.6+) |
__format__ | format(x, ...) (2.6+) |
综合示例
编辑import math
class MyComplex:
"""A complex number""" # Class documentation
classvar = 0.0 # A class attribute, not an instance one
def phase(self): # A method
return math.atan2(self.imaginary, self.real)
def __init__(self): # A constructor
"""A constructor"""
self.real = 0.0 # An instance attribute
self.imaginary = 0.0
c1 = MyComplex()
c1.real = 3.14 # No access protection
c1.imaginary = 2.71
phase = c1.phase() # Method call
c1.undeclared = 9.99 # Add an instance attribute
del c1.undeclared # Delete an instance attribute
print(vars(c1)) # Attributes as a dictionary. vars() 的用法是返回对象的 __dict__ 属性。
vars(c1)["undeclared2"] = 7.77 # Write access to an attribute
print(c1.undeclared2) # 7.77, indeed
MyComplex.classvar = 1 # Class attribute access
print(c1.classvar == 1) # True; class attribute access, not an instance one
print("classvar" in vars(c1)) # False
c1.classvar = -1 # An instance attribute overshadowing the class one
MyComplex.classvar = 2 # Class attribute access
print(c1.classvar == -1) # True; instance attribute acccess
print("classvar" in vars(c1)) # True
class MyComplex2(MyComplex): # Class derivation or inheritance
def __init__(self, re = 0, im = 0):
self.real = re # A constructor with multiple arguments with defaults
self.imaginary = im
def phase(self):
print "Derived phase"
return MyComplex.phase(self) # Call to a base class; "super"
c3 = MyComplex2()
c4 = MyComplex2(1, 1)
c4.phase() # Call to the method in the derived class
class Record: pass # Class as a record/struct with arbitrary attributes
record = Record()
record.name = "Joe"
record.surname = "Hoe"
新风格类
编辑从python 2.2开始支持新风格类(new-style class)。Python 3已经都是新风格类了,即使是按照老风格写的代码。新风格类(new-style class)是指继承自 object 基类的类。在 Python 3 中,所有类都隐式继承自 object,即使你没有显式地写 class MyClass(object):,Python 3 会自动将其作为新风格类处理。
- 新风格类支持 __class__ 属性,这是指向类对象的引用。你可以通过 object.__class__ 来访问当前对象所属的类。
- 新风格类中,super() 函数用来调用父类的方法,它提供了一种调用父类方法的统一方式。特别是在多继承中,super() 可以确保方法解析顺序(MRO,Method Resolution Order)按正确顺序调用。
- 新风格类支持描述符协议(Descriptors),允许自定义如何访问对象的属性。描述符协议主要涉及三个方法:__get__、__set__ 和 __delete__,这些方法在属性访问、设置和删除时会被自动调用。
- 新风格类支持元类(Metaclasses)的概念,元类用于创建类,通过元类,你可以控制类的创建过程、修改类的行为等。
- 新风格类引入了方法解析顺序(MRO),它是多继承时决定方法调用顺序的一种规则。你可以通过 class.mro() 或 super().mro() 查看类的 MRO。
Classic Class:
>>> class ClassicFoo:
... def __init__(self):
... pass
New Style Class:
>>> class NewStyleFoo(object):
... def __init__(self):
... pass
属性
编辑属性(Property)是有getter与setter方法。
>>> class SpamWithProperties(object):
... def __init__(self):
... self.__egg = "MyEgg"
... def get_egg(self):
... return self.__egg
... def set_egg(self, egg):
... self.__egg = egg
... egg = property(get_egg, set_egg)
>>> sp = SpamWithProperties()
>>> sp.egg
'MyEgg'
>>> sp.egg = "Eggs With Spam"
>>> sp.egg
'Eggs With Spam'
>>>
从Python 2.6开始有@property decorator:
>>> class SpamWithProperties(object):
... def __init__(self):
... self.__egg = "MyEgg"
... @property
... def egg(self):
... return self.__egg
... @egg.setter
... def egg(self, egg):
... self.__egg = egg
静态方法
编辑>>> class StaticSpam(object):
... def StaticNoSpam():
... print "You can't have have the spam, spam, eggs and spam without any spam... that's disgusting"
... NoSpam = staticmethod(StaticNoSpam)
>>> StaticSpam.NoSpam()
You can't have have the spam, spam, eggs and spam without any spam... that's disgusting
或使用函数decorator @staticmethod
>>> class StaticSpam(object):
... @staticmethod
... def StaticNoSpam():
... print "You can't have have the spam, spam, eggs and spam without any spam... that's disgusting"
菱形继承的方法搜索顺序
编辑Python 2.2开始,多继承中父类的搜索顺序是广度优先MRO(Method Resolution Order),称为C3算法。这样的新式类有__mro__属性,可以打印出父类搜索顺序。
最佳实践
编辑建议像C++那样使用类。使用<class>.<member>语法,代替__dict__。
封装
编辑私有(属性或方法)名字,是在名字前缀2个或更多下划线并且没有后缀2个或多个下划线。
所有的私有,都不能在类的外部使用。父类的私有属性可以被子类调用吗? 不可以。
Python 其实没有真正的隐藏机制,双下画线只是 Python 的一个小技巧,Python 会“偷偷”地改变以双下画线开头的方法名,会在这些方法名前添加单下画线和类名。因此上面的 __method1() 方法其实可以按_className__method1方式调用(通常并不推荐这么干)。
Doc Strings
编辑鼓励使用
运行时增加方法
编辑增加给类
编辑class Spam:
def __init__(self):
self.myeggs = 5
def cook(self):
print "cooking %s eggs" % self.myeggs
Spam.cook = cook #add the function to the class Spam
eggs = Spam() #NOW create a new instance of Spam
eggs.cook() #and we are ready to cook!
This will output
cooking 5 eggs
增加给实例
编辑class Spam:
def __init__(self):
self.myeggs = 5
eggs = Spam()
def cook(self):
print "cooking %s eggs" % self.myeggs
import types
f = types.MethodType(cook, eggs, Spam)
eggs.cook = f
eggs.cook()
输出为:
cooking 5 eggs
使用一个函数
编辑写一个函数来实现给类实例增加方法:
def attach_method(fxn, instance, myclass):
f = types.MethodType(fxn, instance, myclass)
setattr(instance, fxn.__name__, f)
attach_method(cook, eggs, Spam)
在函数add_method中不能写instance.fxn = f因为它增加了一次函数调用fxn到这个实例