Python3 类

推荐类名用驼峰命名,函数和方法用下划线命名

函数与方法的区别:函数中的数据是显式传递的;方法与对象绑定,方法中的数据是隐式传递的。

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'))
类的专有方法:
__bases__: 返回所有直接父类组成的元组
__subclasses__:返回该类的所有子类组成的列表
__init__ : 构造函数,在生成对象时调用
__del__ : 析构函数,在释放对象时使用
__repr__ : 打印,转换
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__dir__:显示属性名和方法名
__dict__:显示类或实例中的所有属性组成的字典
__len__: 获得长度
__cmp__: 比较运算
__call__: 函数调用
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方运算
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)

你可能感兴趣的:(python,python)