推荐类名用驼峰命名
,函数和方法用下划线命名
函数与方法的区别:函数中的数据是显式传递的;方法与对象绑定,方法中的数据是隐式传递的。
python 的 typing 中的 Union 和 Optional 区别是什么?
assert Union[str, None] == Optional[str]
Union 是 必须是其中之一
Optional 是 可以是其中之一,但也可以不是
self
代表的是类的实例,代表当前对象的地址,而 self.class
则指向类。Python 会自动绑定类方法的第一个参数指向调用该方法的对象。
class CLanguage:
name = "C语言中文网" # 类变量
def __init__(self, v):
CLanguage.url = "http://c.biancheng.net" # 类变量,只有实例化对象时,才会创造
self.value = v # 实例变量
def prt(self):
print("prt函数:", self, self.__class__)
self.temp = "Python" # 实例变量,只有调用该方法时,才会创造
def info(self):
print("info函数:", self)
'''dir() & __dict__
print(dir(CLanguage))
print(CLanguage.__dict__)
print(dir(a))
print(a.__dict__)
'''
# 内存:类{CLanguage, name="C语言中文网"}, 此时不存在类变量url
a = CLanguage(1) # 类实例化为对象a
# 内存:类{CLanguage name="C语言中文网", url="http://c.biancheng.net"}
# 实例a{CLanguage类对象指针, value=1}
print("1", CLanguage.name, id(CLanguage.name))
print("2", a.name, id(a.name)) # 通过实例a访问类变量name
a.name = "Python教程" # 类变量name并没有修改,而添加一个实例变量name
# 内存:类{CLanguage name="C语言中文网", url="http://c.biancheng.net"}
# 实例a{CLanguage类对象指针, name="Python教程", value=1}
print("3", a.name, id(a.name)) # 通过实例a访问实例变量name,屏蔽了同名类变量name
a.name = "放弃Python" # 修改实例变量,通过新的实例变量地址name'掩盖原实例变量name
# 实例a{CLanguage类对象指针, name'="放弃Python", value=1}, 原实例变量name并没有回收
print("4", a.name, id(a.name))
print("5", CLanguage.url, id(CLanguage.url))
CLanguage.url = "www.runoob.com" # 通过类名修改类变量,通过新的类变量地址url'掩盖原类变量url
# 类{CLanguage, name="C语言中文网", url="runoob.com"}
print("6", CLanguage.url, id(CLanguage.url))
print("7", a.url, id(a.url)) # 同时修改a.url
b = CLanguage(2) # 类实例化为对象b
# 类{CLanguage, name="C语言中文网", url="http://c.biancheng.net"}
# 实例b{CLanguage类对象指针,value=2}
print("8", CLanguage.url, id(CLanguage.url)) # 注意为原url的地址,由初始化决定的
print("9", b.value, id(b.value)) # 不存在类变量 CLanguage.value
CLanguage.view = 2000 # 动态添加类变量
# 类{CLanguage, name="C语言中文网", url="http://c.biancheng.net", view=2000}
# 实例b{CLanguage类对象指针,value=2}
print("10", id(CLanguage.view), id(b.view))
b.view = 0 # 添加实例变量view,掩盖同名的类变量
del CLanguage.view # 删除类变量
print("11", b.view, id(b.view)) # 修改,删除类变量view,不会影响同名实例变量view
a.prt() # 通过调用方法添加新的实例变量temp
print("12", a.temp, id(a.temp))
# 动态添加实例方法
c = CLanguage(3)
c.foo = info # # 使用info对clanguage的foo方法赋值(动态绑定方法)
c.foo(c) # Python不会自动将调用者绑定到第一个参数,
# 因此程序需要手动将调用者绑定为第一个参数
# 使用lambda表达式为clanguage对象的bar方法赋值(动态绑定方法)
c.bar = lambda self: print('lambda表达式:', self, self.__class__)
c.bar(c)
类方法 & 静态方法 & 实例方法
- 采用
@classmethod
修饰的方法为类方法;- 采用
@staticmethod
修饰的方法为静态方法;- 不用任何修改的方法为实例方法。
class CLanguage(object):
bar = 1 # 类变量
# 实例方法,至少包含一个self参数,用于 Python 自动绑定调用此方法的实例对象
def __init__(self, name="C语言中文网", url="ttp://c.biancheng.net"):
self.name = name # 实例变量
self.url = url # 实例变量
def printd(self, *arg, **kwarg): # 实例方法
print('instancecmethod:', self, self.name, self.url)
# 类方法,至少需要一个cls参数,用于 Python 自动绑定给类本身
@classmethod
def cmethod(cls):
print('classcmethod:', cls, cls.bar)
@classmethod
def str_to_name_url(cls, str, *arg, **kwarg):
name, url = str.split('+')
return cls(name, url)
# 静态方法,无类似的self, cls参数,Python 不会对它包含的参数做任何类或对象的绑定,即类的静态方法中无法调用任何类属性和类方法
# 只能通过 类名.属性 或 类名.方法 调用
@staticmethod
def smethod(*arg):
print('staticmethod:', CLanguage.bar, arg)
if __name__ == '__main__':
a = CLanguage()
a.printd() # 实例方法,通常会用类对象直接调用
CLanguage.printd(a) # 类名调用实例方法,需要手动给 self 参数传值
a.__class__.printd(a)
CLanguage.cmethod() # 使用类名直接调用类方法(推荐)
a.cmethod() # 使用实例对象调用类方法(不推荐)
str = "Python 教程+http://c.biancheng.net/python"
b = CLanguage.str_to_name_url(str)
b.printd()
CLanguage.smethod("C语言中文网", "http://c.biancheng.net") # 使用类名直接调用静态方法
a.smethod(CLanguage.bar) # 使用类对象调用静态方法
私有类变量 & 私有实例变量 & 私有方法
class Site:
def __init__(self, name=None, url=None):
print('__init__() in Site')
self.name = name # public 实例变量
self.__url = url # private 实例变量
def __del__(self):
print('__del__() in Site')
def __repr__(self):
print('__repr__() in Site')
return 'Site[name='+self.name+', url='+self.__url+']'
def __call__(self, name, url):
print('__call__() in Site')
print(name, url)
def __foo(self): # private 类方法
print('这是私有方法.')
def foo(self): # public 类方法
print('这是公共方法.')
self.__foo()
class sit(Site):
def __init__(self):
print('__init__() in sit')
def __del__(self):
super().__del__() # 继承时,必须显式调用父类__del__释放继承于父类的资源
print('__del__() in sit')
x = Site('菜鸟教程', 'www.runoob.com') # 实例化类
print(x) # 由于 __repr__() 方法
x.foo() # 正常输出
# x.__foo() # 报错,外部不能调用 private 方法
# print(x.__url) # 报错,实例不能访问 private 变量
# Python采用自动引用计数(ARC)的方式实现垃圾回收机制。
# 每当删除一个 Python 实例对象的引用,计数器减1。
# 当一个 Python 实例对象的计数器值为0,自动调用__del__()方法。
#
y = x
del x
print('删除x')
del y # 之后才调用 __del__
print('删除y')
a = sit()
del a
b = Site() # 通过实现__call__(), Site 实例对象b变为可调用对象。
print(b.__call__('C语言中文网','http://c.biancheng.net'))
print(b('C语言中文网','http://c.biancheng.net'))
类的专有方法:class A:
pass
class B:
pass
class C(A, B):
pass
print('类A的所有父类:', A.__bases__)
print('类B的所有父类:', B.__bases__)
print('类C的所有父类:', C.__bases__)
print('类A的所有子类:', A.__subclasses__())
print('类B的所有子类:', B.__subclasses__())
property() 类 && @property 描述符
1. 对象方法可以像属性一样访问
class DataSet(object):
@property
def method_with_property(self): # 含有@property
return 15
def method_without_property(self): # 不含@property
return 15
l = DataSet()
print(l.method_with_property) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。 结果:15
print(l.method_without_property()) #没有加@property , 必须使用正常的调用方法的形式,即在后面加() 结果:15
2. 与私有属性配合使用
@property
为语法糖
可以将一个方法变成属性调用,起到检查和访问属性的作用
# 1. 常规方式
class Person(object):
def __init__(self, name, age=18):
self.name = name
self.__age = 18 # private 实例变量,不能用(对象.__age)访问,必须调用(对象.方法)访问
def get_age(self):
return self.__age
def set_age(self, age):
if age< 18:
print('年龄必须大于18岁')
return
self.__age = age
def del_age(self):
self.__age = 18
xm = Person('xiaoming')
print(xm.get_age())
xm.set_age(20)
print(xm.get_age())
xm.del_age()
print(xm.get_age())
# 2. @property 更简单的方式,私有属性的访问
class Persion(object):
def __init__(self, name, age=18):
self.name = name
self.__age = age
@property
def age(self):
"""Person a private attribute"""
return self.__age
@age.setter
def age(self, age):
if age<18:
print("年龄必须大于18岁")
return
self.__age = age
return
@age.deleter
def age(self):
self.__age = 18
xm = Persion("xiaoming")
print(xm.age)
xm.age = 20
print(xm.age)
del xm.age
print(xm.age)
# 3. 添加 property类
class Person(object):
def __init__(self, name, age=18):
self.name = name
self.__age = 18
def get_age(self):
return self.__age
def set_age(self, age):
if age< 18:
print('年龄必须大于18岁')
return
self.__age = age
def del_age(self):
self.__age = 18
age = property(get_age, set_age, del_age, "form Person age attribute") # 添加 prooerty 类
xm = Person('xiaoming')
print(xm.age)
xm.age = 20
print(xm.age)
del xm.age
print(xm.age)
super() 函数是用来调用父类的一个方法。在多继承中使用时,需要考虑 MRO(即Method Resolution Order)的问题。super() 函数获取的是 MRO 列表中的下一个类。
class A(object):
def __init__(self):
self.n = 10
def minus(self, m):
print('minus in class A start',
' self is {0} @A.minus.format(self)'.format(self))
self.n -= m
print('minus in class A end', " self.n=", self.n)
class B(A):
def __init__(self):
self.n = 7
def minus(self, m):
print('minus in class B start',
' self is {0} @B.minus.format(self)'.format(self))
super(B, self).minus(m)
self.n -= 2
print('minus in class B end', " self.n=", self.n)
class C(A):
def __init__(self):
self.n = 12
def minus(self, m):
print('minus in class C start',
' self is {0} @C.minus.format(self)'.format(self))
super(C, self).minus(m)
self.n -= 5
print('minus in class C end', " self.n=", self.n)
class D(B, C):
def __init__(self):
self.n = 15 # 实例变量
def minus(self, m):
print('minus in class D start',
' self is {0} @D.minus.format(self)'.format(self))
super(D, self).minus(m)
self.n -= 2
print('minus in class D end', " self.n=", self.n)
print('The MRO of class D is :', D.__mro__) # D.mro()
d = D()
d.minus(2)
print(d.n)
class Bird:
def isWing(self):
print("鸟有翅膀")
def fly(self):
print("鸟会飞")
class Ostrich(Bird):
def fly(self): # 重写Bird类的fly()方法
print("鸵鸟不会飞")
ostrich = Ostrich() # 创建Ostrich对象
ostrich.fly() # 调用 Ostrich 类中重写的 fly() 类方法
Bird.fly(ostrich) # 调用 Bird 类中的 fly() 方法, 使用类名调用时,需要手动为self参数赋值
重载运算符,指的是在类中定义并实现一个与运算符对应的处理方法,这样当类对象在进行运算符操作时,系统就会调用类中相应的方法来处理。
Python 常用重载运算符
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self): # 用于将值转化为字符串形式
return 'Vector(%d, %d)' % (self.a, self.b)
def __add__(self, other):
return Vector(self.a+other.a, self.b+other.b)
def __lt__(self, other):
return True if (self.a**2+self.b**2) < (other.a**2+other.b**2) else False
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1<v2)
print(v1+v2)
class DictDemo:
def __init__(self, key, value):
self.dict = {}
self.dict[key] = value
def __getitem__(self, key):
return self.dict[key]
def __setitem__(self, key, value):
self.dict[key] = value
def __len__(self):
return len(self.dict)
dictDemo = DictDemo('key0', 'value0')
print(dictDemo['key0']) # value0
dictDemo['key1'] = 'value1'
print(dictDemo['key1']) # value1
print(len(dictDemo)) #2
class WhoSay:
def say(self, who):
who.say()
class CLanguage:
def say(self):
print("调用的是 CLanguage 类的 say 方法")
class CPython(CLanguage):
def say(self):
print("调用的是 CPython 类的 say 方法")
class CLinux(CLanguage):
def say(self):
print("调用的是 CLinux 类的 say 方法")
a = CLanguage()
a.say()
a = CPython()
a.say()
a = CLinux()
a.say()
b = WhoSay()
b.say(CLanguage())
b.say(CPython())
b.say(CLinux())
实例化对象个数固定的类,可以用枚举类来定义。
from enum import Enum
class Color(Enum):
# red, green, blue为枚举成员
red = 1
green = 2
blue = 3
'''
Color = Enum("Color", ('red', 'green', 'blue'))
'''
# 调用枚举成员的 3 种方式
print(Color.red, Color['red'], Color(1))
for color in Color:
print(color)
# 调用枚举成员中的 value 和 name
print(Color.red.value, Color.red.name)