Python中的数据类型都属于类。int、str、list都是Python定义好的数据类型类。
print(type(list))#
print(type(list()))#
class 类名(): pass #类名 要求首字母大写 #()可写可省略。 #pass在这里只是用来保证语法不报错,本来应该是类体
class Person(): pass class Cat(): pass
对象名=类名() #对象名是一个变量名,数据类型是 该类的类型 #这个括号一定要加,不加的话相当于给类名起了一个临时别名
示例:
class Person: pass p=Person() y=Person print(type(p),type(y),type(y())) #
#p是Person类型的对象,y是type类型的对象,类名就是一个type对象
__init__(self) 定义 实例方法,指定初始化类的参数(默认构造函数)
①类中的函数称为方法。
②实例属性和实例方法指的是根据不同的实例变量值和方法的结果是不同的。类属性是类固有的,每个实例都共用这一个属性。实例属性必须是在__init__中定义的。并且,实例属性在该初始化方法中定义之后,可以在整个类中用self.来使用。
③初始化方法指定了一个类在定义对象时需要传递的参数,self参数是对象自带的,不需要传入。如果不用指定实例属性,也可以没有初始化方法。
类属性,使用类名打点调用,包括类内部也是如此。当然可以用实例属性的对象名打点调用。
class Student: #类属性:定义在类中的变量,方法外的变量 school='Jlu' #初始化方法 def __init__(self,name,age):#name和age是方法的参数,可以忽略,局部变量 self.name=name # self打点的 self.name是一个实例属性,name是局部变量,将局部变量的值赋值给实例属性 self.age=age #实例属性的名称和局部变量的名称可以相同 print(self.school)
实例属性,使用对象名打点调用
class Student: #类属性:定义在类中的变量 school='Jlu' #初始化方法 def __init__(self,name,age): self.name=name self.age=age #实例方法 def show(self): print(f'我叫:{self.name},今年{self.age}岁了,来自{Student.school}')
静态方法是类有的,而不是实例有的,不能使用实例属性,也不能调用实例方法。
静态方法是也可以用对象打点调用,不可以用类打点调用的。
class Student: school='Jlu' def __init__(self,name,age): self.name=name self.age=age def show(self): print(f'我叫:{self.name},今年{self.age}岁了') what() #× @staticmethod def what(): print('This is a staticmethod') stu=Student('22',2) stu.show() #√
class Student: school='Jlu' def __init__(self,name,age): self.name=name self.age=age def show(self): print(f'我叫:{self.name},今年{self.age}岁了') @staticmethod def what(): print('This is a staticmethod') @classmethod #这个@只作用于下面第一个函数 def cm(cls):#cls是类方法自带的,正如实例方法自带self print('This is a classmethod') stu=Student('22',2) stu.cm() Student.cm() #输出This is a classmethod
类方法可以通过类名来直接调用。也可以用对象名打点调用。
stu=Student('Yorelee',21) #实例属性,使用对象名打点调用 print(stu.name,stu.age,Student.school) #实例方法,使用对象名打点调用,除了self外,需要传入所需的参数 stu.show() #类方法,使用类名打点调用,需要传入所需的参数 Student.cm() #静态方法,使用类名打点调用,需要传入所需的参数 Student.what()
class Student: school='Jlu' def __init__(self,name,age): self.name=name self.age=age def show(self): print(f'我叫:{self.name},今年:{self.age}岁了') #创建对象 stu1=Student('李泽斌',21) stu2=Student('王一一',20) stu3=Student('Jack Sparrow',40) stu4=Student(age=23,name='Rose') Student.school='Haffman' #将对象存储列表当中 lst=[stu1,stu2,stu3,stu4] for i in lst: print(i.name,i.age,end='-->') i.show() print(Student.school) ''' 李泽斌 21-->我叫:李泽斌,今年:21岁了 王一一 20-->我叫:王一一,今年:20岁了 Jack Sparrow 40-->我叫:Jack Sparrow,今年:40岁了 Rose 23-->我叫:Rose,今年:23岁了 Haffman '''
stu=Student('yore',21) stu1=Student('Sweet',20) #动态绑定实例属性,直接 对象.实例属性名 stu.gender='男' #只为stu绑定了一个属性,stu1没有 def introduce(): print('我是一个普通的函数,我被动态绑定成stu1对象的方法') stu1.My_intro=introduce #函数赋值 introduce() stu1.My_intro()#很像是给了introduce一个别名 可以调用而已 '''这里像极了普通变量 和 普通函数变量的赋值,不过不一样的是这里用了对象.'''
单下划线开头:防君子不防小人
双下划线开头:表示私有成员,只允许本类访问。
首尾双下划线:特殊方法
class Student: def __init__(self,name,age,gender): self._name=name #受保护的,只允许本类和子类访问 self.__age=age #私有的,只能类本身去访问 self.gender=gender #普通实例属性,内外,子类都可以访问 def _fun1(self): pass #子类和本身可以使用 def __fun2(self): pass #只有定义的类可以访问 def show(self): #普通的实例方法 self._fun1() slef.__fun2()
与C++不一样,这里的私有成员是可以被访问的,在类外
对象名._类名__函数名/实例属性名。(不推荐使用)
print(dir(stu))#使用内置函数dir() 查看对象中的所有对象 #['_Student__age', '_Student__fun2', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_fun1', '_name', 'gender', 'show'] #你会发现,私有成员的名字变成了'_Student__age',还是可以使用! #并且在实例方法里面还是可以调用私有成员,这和C++中强制不允许不一样
stu=Student('陈梅酶','21','女') print(stu._Student__age) stu._Student__fun2() #可以这样访问的原理是,
属性的设置
使用装饰器@property,可以将方法转换成属性使用,这样可以使用私有成员。但是不可以修改。
使用装饰器@属性名.setter,可以修改私有成员
class Student: def __init__(self,name): self.__name=name @property def name(self):#装饰器修饰之后,在使用该函数时可以不加括号 return self.__name @name.setter def name(self,value): if value=='霸道总裁': print("别搞") else: self.__name=value stu=Student('Yore') print(stu.name) #实际上是调用方法name(),所以不能修改值 stu.name='霸道总裁' stu.name='王一一'
子类可以使用父类的公有和保护部分内容。
如果子类只有一个父类可以使用super()来调用父类的方法。
class Person: def __init__(self,name,age): self.name=name self.age=age def show(self): print(f'大家好,我是{self.name}') class Student(Person): def __init__(self,name,age,stuno): super().__init__(name,age) #调用父类的初始化方法。 self.stuno=stuno class Doctor(Person): def __init__(self,name,age,deptname): super().__init__(name,age) self.deptname=deptname stu=Student('yor',20,'0328') stu.show() doctor=Doctor('Doc.strange',20,'Com.Si.') doctor.show()
import torch import torch.nn as nn '''----------------------生成器定义代码---------------------''' class TriggerGenerator(nn.Module):#继承机器学习父类,拥有其公有内容和保护内容 def __init__(self, input_size, output_size): super().__init__()#在Python3中,super()不需要指定类和self self.fc1 = nn.Linear(input_size, 128) self.fc2 = nn.Linear(128, output_size) def forward(self, x): x = F.relu(self.fc1(x)) x = self.fc2(x) return x
多继承
如果一个子类继承多个父类,用父类名来调用父类方法,需要传入self来指定子类对象。
class FatherA(): def __init__(self,name): self.name=name def showA(self): print("I am FahterA") class FatherB: def __init__(self,age): self.age=age print("PPAP") def showB(self): print("B") class Son(FatherA,FatherB): def __init__(self,name,age): FatherA.__init__(self,name) FatherB.__init__(self,age) son=Son('陈梅酶',20) son.showA() son.showB() ''' PPAP I am FahterA B '''
方法重写
方法重写必须方法名称和父类方法名称相同才能重写。子类重写了先调用子类的,没重写调用父类的。
class FatherB: def __init__(self,age): self.age=age print("PPAP") def showB(self): print("B",end=' ') class Son(FatherB): def __init__(self,name,age): FatherB.__init__(self,age) def showB(self): super().showB() print("is my father") son=Son('陈梅酶',20) son.showB() ''' PPAP B is my father '''
C++中的多态基于继承,虚机制。但是python中只关心方法,不关心继承,只关心运行时具体是哪个对象。
class Person: def eat(self): print("人喜欢吃五谷杂粮") class Cat: def eat(self): print("猫喜欢吃鱼") class Dog: def eat(self): print("狗喜欢啃骨头") def fun(obj): obj.eat() per=Person() cat=Cat() dog=Dog() fun(per) fun(cat) fun(dog) ''' 人喜欢吃五谷杂粮 猫喜欢吃鱼 狗喜欢啃骨头 '''
如果一个类没有父类,默认继承object类。
class Person(object): def __init__(self,name,age): self.name=name self.age=age def show(self): print(f'大家好,我叫:{self.name},我今年:{self.age}岁') per=Person('wq',21) #创建对象时,会自动调用__init__方法 print(dir(per)) #显示对象per的所有方法和属性,可以发现object类中的内容 ''' ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'show'] '''
__new__()先执行 创建对象,开辟空间,然后执行__init__()给对象赋值,都是自动调用的。
class Person(object): def __init__(self,name,age): self.name=name self.age=age def __str__(self):#方法重写 return "Love U U" per=Person('l',2) print(per) #如果子类没有进行方法重写,那么默认调用__str__()返回地址,否则调用方法重写的 print(per.__str__())#也可以显式调用
特殊方法前后有两个下划线,与开头双下划线的私有方法不同,这种特殊方法可以在类外调用。
a=10 #Python当中一切皆对象 print(dir(10)) print(a+10) print(a.__add__(10)) print(a.__sub__(1)) #1.__sub__(1)是不行的 ''' ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes'] 20 20 '''
obj.表示对象调用,实例属性。class.表示类调用,类属性。
层次结构表示的是都继承了谁,父类默认为第一个。
子类列表是方法,返回值为一个列表,并且只返回直属的儿子,孙子不在里面。
class A: school='JJ' class B: pass class D: school='J' class C(A,B,D): def __init__(self,name,age): self.name=name self.age=age a=A() b=B() c=C('Yore',20) print('对象a的实例属性字典:',a.__dict__) print('对象b的实例属性字典:',b.__dict__) print('对象c的实例属性字典:',c.__dict__) ''' 对象a的实例属性字典: {} 对象b的实例属性字典: {} 对象c的实例属性字典: {'name': 'Yore', 'age': 20} ''' print('对象a所属的类:',a.__class__) print('对象b所属的类:',b.__class__) print('对象c所属的类:',c.__class__) ''' 对象a所属的类:
对象b所属的类: 对象c所属的类: ''' print(A.__bases__,A.__base__) print(B.__bases__,B.__base__) print(C.__bases__,C.__base__) ''' ( ,) ( ,) ( , , ) ''' print(A.__mro__) print(B.__mro__) print(C.__mro__) ''' ( , ) ( , ) ( , , , , ) ''' object.__subclasses__() A.__subclasses__() C.__subclasses__() ''' [ , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] [ ] [] '''
变量的赋值:A=B,该变量A的指针直接指向B指向的内存空间
浅拷贝:A=copy.copy(B),给A开辟一个新的空间,A指向该空间,该空间中的内容完全拷贝了B指向的空间的内容。
深拷贝:A=copy.deepcopy(B),给A给A开辟一个新的空间,A指向该空间,该空间中的对象同样开辟新的空间。
都不调用__init__()
红色com1是赋值
蓝色com2是浅拷贝
橙色com3是深拷贝
普通的不可变数据类型的赋值,只会有一个对象,所有变量指向同一个变量。
可变组合数据类型会创建一个副本,赋值之后产生一个新的对象,该对象指向新对象。
通过以下例子你会发现,com和com1指向的地址是一样的,给com1.a赋值之后,com.a的值也发生了变化,说明com和com1真的指向的是同一个内存地址,本质上属于同一个变量。
class A: pass class B: pass class Computer: def __init__(self,a,b): self.a=a self.b=b a=A() b=B() a1=A() com=Computer(a,b) com1=com print(com,com.a,com.b) print(com1,com1.a,com1.b) com1.a=a1 print(com1,com1.a,com1.b) print(com,com.a,com.b) ''' <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48C90> <__main__.B object at 0x0000020FB8E48CD0> <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48C90> <__main__.B object at 0x0000020FB8E48CD0> <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48D10> <__main__.B object at 0x0000020FB8E48CD0> <__main__.Computer object at 0x0000020FB8E48D50> <__main__.A object at 0x0000020FB8E48D10> <__main__.B object at 0x0000020FB8E48CD0> ''' lst1=[1,2,3] lst2=[1,2,3] print(id(lst1),id(lst2)) lst2.append(4) print(id(lst2)) ''' 1377912245248 1377913237952 1377913237952 ''' t1=tuple([1,2,3]) t2=t1 print(id(t1),id(t2))
使用copy模块,子对象类型不拷贝。也就是说这个时候两个变量指向的是两个不同的对象,但是原来的对象中的对象仍然指向同一个对象。
import copy class A: pass class B: pass class Computer: def __init__(self,a,b): self.a=a self.b=b a=A() b=B() com=Computer(a,b) com1=copy.copy(com) print(com,com.a,com.b) print(com1,com1.a,com1.b) ''' <__main__.Computer object at 0x0000019D2E6793D0> <__main__.A object at 0x0000019D2E679210> <__main__.B object at 0x0000019D2E678A50> <__main__.Computer object at 0x0000019D2E67A7D0> <__main__.A object at 0x0000019D2E679210> <__main__.B object at 0x0000019D2E678A50> '''
com和com1指向了内存中不同的对象,内存地址不同,但是他们中的对象仍然指向的是同一个对象。相当于给com1开辟了一个新的空间,里面的内容仍然完全copy了com的内容。
import copy class A: pass class B: pass class Computer: def __init__(self,a,b): self.a=a self.b=b a=A() b=B() com=Computer(a,b) com1=copy.deepcopy(com) print(com,com.a,com.b) print(com1,com1.a,com1.b) ''' <__main__.Computer object at 0x0000028D1A3B93D0> <__main__.A object at 0x0000028D1A3B9210> <__main__.B object at 0x0000028D1A3B8A90> <__main__.Computer object at 0x0000028D1A3BBC10> <__main__.A object at 0x0000028D1A3D3FD0> <__main__.B object at 0x0000028D1A3D3E50> '''