python 完全面向对象_Python的面向对象

面向对象目的:

写出有意义的面向对象的代码,其作用就是封装代码

定义时注意:

命名规范 Student、StudentPages

类体不能什么都不写,要写pass

定义示例:

class Student():

# 开始类体的编写

name = ''

age = 0

def print_file():

print('age = '+str(age))

stu = Student() #不需要使用new 来实例化这个类

调用类的方法:

stu.print_file()

注意上述调用会报错

# TypeError: print_file() takes 0 positional arguments but 1 was given

做如下修改:

def print_file(self):

print('age = '+str(age))

仍然报错,报错age没有定义

继续修改,改完正确运行

def print_file(self):

print('age = '+str(self.age))

正确示例:

class Student():

name = ''

age = 0

def print_file(self):

print('age = '+str(self.age))

stu = Student()

stu.print_file()

# 或者直接用:Student().print_file()

注意:

上述类体中,对于print_file函数,不能在类体里调用

写类的模块,最好是只写类,然后通过其他模块来实例化调用什么的

from c1 import Student

Student().print_file()

Student().age

注意:

如果c1.py中同时包含对Student类的实例化和调用,那么上述import时也会执行c1.py中的示例化和调用

所以,最好是模块和类分开,便于调用时的清晰

方法 和 函数:区别:

方法是语言设计层面的考量,应用起来没什么区别

类中的函数应该叫‘方法’,模块中的函数就叫‘函数’

类中的变量应该叫‘数据成员’,模块中变量叫‘变量’

类和对象 通过实例化联系在一起

什么是类:

就是数据及一些操作的有意义的封装,可以体现出数据特征和行为特征

行为要联系主体,体现在类的设计中要具有现实意义

什么是对象:

表示具体的类对象,类本身可以实例化多种多样的对象

通过实例化来创造对象的多样性,依靠类的构造函数实现

class Student():

name = ''

age = 0

def __init__(self): # 至少需要添加self参数

print('init')

def print_file(self):

print('age = '+str(self.age))

stu = Student() #构造函数在实例化时自动调用

stu.__init__()

#构造函数也可以调用,跟普通函数类似,但是不推荐这样用

但是:对于构造函数 只允许返回None,返回其他则报错

为构造函数添加参数:

def __init__(self,param1,param2):

此时实例化类时,必须传入两个值:stu = Student('a','b')

构造函数通常的用法:

来修改类的数据特征,即重置类的成员变量

class Student():

name = ''

age = 0

def __init__(self, name, age): # 至少需要添加self参数

name = name

age = age

上面的代码不报错,但是不能修改name和age的值,并不是因为变量作用域的问题

注意:

类的变量的作用域 与 模块变量的作用域 完全不同!

要注意区别类的行为和模块的行为

类变量 实例变量:代码示例:

class Student():

name = '' name 是类变量:与类相关

age = 0 age 是类变量:与类相关

def __init__(self, name, age):

self.name = name self.name 实例变量:与对象相关

self.age = age self.age 实例变量:与对象相关

s1 = Student('a',1) 作为实例变量传入

s2 = Student('b',8) 作为实例变量传入

注意上述self可以换成任意名称:

def __init__(this, name, age):

this.name = name

this.age = age

换成this也是对的,但是推荐使用默认的 self

使用区别:

对象名.成员变量 取决于实例化时的构造

类名.成员变量 只跟类有关,不可改变

应用场景:

比如定义一个狗类叫做ClassA:

里面有成员变量 动物种类、狗品种、狗毛色

有构造函数,参数为品种、毛色,但动物种类变量就等于“狗”,构造时不修改

实例化时借助构造函数,得到N个不同的狗对象ObjN,可以对应现实世界中不同的狗个体

此时,ObjN.品种,就是此狗对象的对象属性

而ClassA.动物类型,表明此类的特征属性,表示共同特性或者不属于个体特性的变量就可以作为类的成员变量

(类的机制)

类变量和实例变量的特性示例代码:

class Student():

name = '类变量name'

age = 0

def __init__(self, name, age): # 至少需要添加self参数

name = name

age = age

obj = Student('实例变量name','实例变量age')

print(obj.name) #打印类变量name

print(Student.name) #打印类变量name

print(obj.__dict__) #打印{}

print(Student.__dict__) #打印{'name': '类变量name', '__doc__': None, '__weakref__': , '__init__': , '__module__': '__main__', '__dict__':, 'age': 0}

寻找相关变量的机制:

如果尝试去访问对象的一个成员变量

首先会在对象的变量列表obj.__dict__里去查找有没有

否则,到类的变量列表Student.__dict__去寻找

否则继续去类的父类中去寻找

示例代码:

class Student():

name = '类变量name'

age = 0

def __init__(self, name, age): # 至少需要添加self参数

self.name = name

self.age = age

obj = Student('实例变量name','实例变量age')

print(obj.name) #打印类变量name

print(Student.name) #打印实例变量name

print(obj.__dict__) #打印{'age': '实例变量age', 'name': '实例变量name'}

解释:

修改为self.name之后,则是实例的变量,在构造函数中必须赋值给实例的变量

定义实例方法例如构造函数时,需要self出现,但是调用实例方法时不需要出现self

注意:

self和实例、对象绑定,与类无关

实例、对象可以调用的方法叫:实例方法,参数第一个必须为 self保留

self可以换成其他名字,比如this,但位置必须是第一参数

解释:

意思是实例方法第一个参数应该保留,具体叫self还是this或其他无所谓,但推荐用 self

Python的类--- 变量 --- 类变量

--- 实例变量

--- 方法 --- 实例方法 self + .操作符 -> 修改实例变量

--- 类方法

--- 静态方法

--- 构造函数(特殊的实例方法,只是默认调用)

实例方法要操作变量:

--- 实例方法 self + .操作符 -> 修改实例变量

例如:

class Student():

name = '类变量name'

age = 0

def __init__(self, name1, age): # 至少需要添加self参数

self.name = name1

self.age = age

print(self.name) #访问的实例变量

print(name)

#这也是访问的实例变量,但是访问的是形参name,如果形参不是name,那就会报错

#print(__dict__)

obj = Student('实例变量name','实例变量age')

打印:

实例变量name

实例变量name

注意:

查找变量列表__dict__只能在外部调用时访问,在实例方法内无法打印

实例方法中,方法参数不要和类变量名相同

类变量定义时,不要与类内置变量重名

--- 实例方法 修改类变量

例如:

class Student():

name = '类变量name'

age = 0

def __init__(self, name1, age): # 至少需要添加self参数

print('形参name1:'+name1)

self.name = name1

print('修改实例变量:'+self.name)

print('访问类变量法一:'+Student.name)

print('访问类变量法二:'+self.__class__.name) #注意self.__class__的使用

obj = Student('实例变量name','实例变量age')

输出:

形参name1:实例变量name

修改实例变量:实例变量name

访问类变量法一:类变量name

访问类变量法二:类变量name

--- 实例方法操作类变量 完成类变量的变化

class Student():

sum = 0

name = '类变量name'

age = 0

def __init__(self, name1, age): # 至少需要添加self参数

self.__class__.sum += 1

print('类变量sum变为:'+str(self.__class__.sum))

obj1 = Student('Tom',13)

obj2 = Student('Kimmy',24)

obj3 = Student('Jack',18)

输出:

类变量sum变为:1

类变量sum变为:2

类变量sum变为:3

注意:

实例方法通常是操作实例变量的,但是也可以操作类变量,引出:专门操作类变量的方法

类方法:定义规范:

@classmethod #使用装饰器@classmethod来定义一个类方法

def plus_sum(cls): #类方法的参数必须含一个cls参数

pass

重新完成上述类变量的修改:

class Student():

sum = 0

def __init__(self,param):

pass

@classmethod

def plus_sum(cls): # cls仍然可以改成别的名字,不建议更改

cls.sum += 1

print(cls.sum)

类方法的调用

stu = Student(1)

stu.plus_sum() # 打印 1

stu = Student(2)

stu.plus_sum() # 打印 2

stu = Student(3)

stu.plus_sum() # 打印 3

再次强调:

实例方法关联的是对象,类方法关联的是类本身

另外,两者有时候都可以完成参数修改,但是要是操作有“意义”有时就需要区分类方法和实例方法,例如与对象无关的操作就应该使用类方法

即,对象最好不要调用@classmethod 类方法(虽然不报错,但是缺失实际意义)

静态方法定义规范:

@staticmethod

def add(x,y):

print("这是一个静态方法")

与其他方法的区别:

静态方法中没有强制参数

实例方法中,self 参数代表对象本身

类方法中 cls 代表类本身

一个对象或以各类都可以调用静态方法

示例:

class Student():

sum = 0

name ='Lei'

def __init__(self,param):

self.name = param

@classmethod

def plus_sum(cls):

cls.sum += 1

print(cls.sum)

#print(self.name) # 类方法不可以引用实例变量

@staticmethod

def add(x,y):

#print(self.name) # 静态方法不可以引用实例变量

print(Student.sum) # 静态方法可以访问类变量

print('这是一个静态方法')

#调用

stu = Student(1)

stu.add(1,1) # 对象调用静态方法 且访问了类变量 不可以引用实例变量

Student.add(1,1) # 类调用静态方法 且访问了类变量 不可以引用实例变量

stu.plus_sum() # 对象调用类方法 且访问了类变量 不可以引用实例变量

Student.plus_sum() # 类调用类方法 且访问了类变量 不可以引用实例变量

注意:

静态方法不要经常使用,与类的关联性不强,与普通函数无区别

类成员的可见性对于下面示例:

class Student():

sum = 0

def __init__(self,param,param1):

self.name = param

self.age = param1

self.score = '0'

print('初值为:'+self.score)

def marking(self,score):

self.score = score

print('修改后:'+str(score))

def do_hmwork(self):

pass

def do_eng_hmwork(self):

pass

s = Student(1,2) # 将socre参数隐藏,不暴露score的直接赋值

s.score = -1

#不推荐方式,直接修改参数,这样没法进行相关过滤,不应该通过直接访问的方式修改

print('修改后:'+str(s.score))

#正确方法:所有访问应该通过方法操作变量,可以在方法中对输入进行判断,进而保护数据

s1 = Student(1,2)

s1.marking(-1)

注意:

上述marking方法之外,仍然可以通过 s.score = -1 来直接赋值,

原因:

上述变量和方法全部都是公开的

Python控制变量的可见性(读、写): 公开public 私有private

方式:

私有变量:__私有变量名

私有函数:__marking()

注意:

对于构造函数,因为__init__右边也有下划线,这样不会被识别为私有

示例:

class Student():

sum = 0

def __init__(self,param,param1):

self.name = param

self.age = param1

self.__score = '0'

print('初值为:'+self.__score)

def __marking(self,score): #添加双下划线

self.__score = score

print('修改后:'+str(self.__score))

s1 = Student(1,2)

#s1.__marking(-1) # 访问私有方法报错:'Student' object has no attribute '__marking'

s1.__score = -1

print(s1.__score) # 访问私有变量,成功修改

#上述原因是:实际是利用py得动态属性,通过点的方式新添加了一个__score变量,原有私有变量并没有修改

#下面直接访问会发现 访问报错 :'Student' object has no attribute '__score'

s2 = Student(1,2)

print(s2.__score)

可以利用__dict__内容验证:

print(s1.__dict__)

输出:

{'name': 1, '_Student__score': '0', 'age': 2,'_score':-1}

print(s2.__dict__)

输出:

{'name': 1, '_Student__score': '0', 'age': 2}

分析上述发现:

其实私有变量会被改名,此处由__score变为了_Student__score,所以访问原名是访问不到的

比较两次打印,会发现s1.__score = -1 这句话其实会添加一个__score变量,而没有修改原来的score。因为原来的socre已经被改名了

上述发现:

其实Python没有完善的私有变量机制,其仅仅是通过改名,如果使用_Student__score来操作,仍然可以完成修改

面向对象的特性:继承三大特性:继承、封装、多态

封装:类就是从现实世界的角度对变量和方法进行封装,很抽象比较难讲清楚

类的组成:变量和方法

继承作用:避免定义重复的方法和重复的变量

推荐一个模块创建一个类

对于以下示例:

c2模块的Human代码如下:

class Human():

sum = 0

def __init__(self, name, age):

self.name = name

self.age = age

def get_name(self):

print(self.name)

子类Student如下:

from c2 import Human

class Student(Human): # 标准的继承方法

sum = 0

def __init__(self,name,age):

self.name = name

self.age = age

self.__score = 0

print('初值为:'+str(self.__score))

s = Student('Tom',13) # 实例化子类时,要按照父类的构造函数传参

print(s.sum)

print(Student.sum) # 打印 0 表示子类继承了父类的类变量

print(s.name) # 打印 Tom 表示子类继承了父类的实例变量

print(s.age) # 打印 13 表示子类继承了父类的实例变量

s.get_name() # 打印 Tom 表示子类继承了父类的实例方法

注意:

上述只是将Human父类的变量和方法提取到了子类中

Python允许多继承,一个子类可以有多个父类,一般用不到

进一步:

现在子类有自己独有的方法和变量

例如:Student类有school变量,那么其构造函数为school+父类构造参数

在子类里调用父类的函数,示例:

from c2 import Human

class Student(Human): # 标准的继承方法

def __init__(self,school,name,age): # 父类构造参数是name age

self.school = school

Human.__init__(self,name,age) #直接调用父类构造函数 传参

#注意此处父类构造参数要加上self,此处是!普通函数的调用!,传参缺一不可,self必不可少

s = Student('YangTz','Tom',13)

print(s.school) # 正确打印YangTz

print(s.name) # 正确打印Tom

print(s.age)# 正确打印13

开闭原则:

对扩展是开放的,对更改本身是关闭的

注意:

Human.__init__(self,name,age)

上述使用类,调用了实例方法,其实不推荐这样做,如果类调用一个实例方法,那么实例方法的 self 参数会成为一个普通参数,调用时应该被传入方法内

现在对于上述代码,如果父类改变,那么代码中涉及的地方全都要改,违反了开闭原则

引出:super() 通用调用方法,修改为:

super(Student,self).__init__(name,age)

注意:

这样修改父类时不需要修改这里的代码

super()目的是继承父类的同名方法,如__init__()或一些公共方法

对于一个普通实例方法do_something(self),如果其和父类方法同名,那么会优先调用子类的此方法

但是如果修改为

def do_homework(self):

super(Student,self).do_homework()

那么此时表示子类的该实例方法继承了父类的该方法,此时调用会执行父类的do_something()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值