python基础——17(面向对象)
一、名称空间
名称空间有内置名称空间,全局名称空间,局部名称空间。它是用来存放名字与值对应关系的地方。
test.py文件:
num = 10
def fn():
print("fn run")
def func():
a = 10
print(locals()) # 以字典类型返回当前位置的全部局部变量{'a': 10}
func()
print(func.__dict__) # {}
a = 10
# print(a.__dict__) # 'int' object has no attribute '__dict__'
ls = [1, 2]
# print(ls.__dict__) # 'list' object has no attribute '__dict__'
能产生名称空间的有:文件 | 函数 | 类
能产生名称空间的对象有__dict__这个值,通过该值可以访问到名称空间。
print(globals()) # globals()返回的是全局变量的字典
# 名称空间的使用
import test
# 两种调用方式本质是一样的
print(test.num) # 10
print(test.__dict__['num']) # 10
test.__dict__['fn']() # fn run
test.fn() # fn run
def fun1():
pass
fun1.__dict__['index'] = 10000
print(fun1.__dict__) # {'index': 10000}
print(fun1.__dict__['index']) # 10000
fun1.add = lambda n1, n2: n1 + n2
print(fun1.__dict__) # {'index': 10000, 'add': <function <lambda> at 0x000000D8066F9B70>}
print(fun1.add(10, 20)) # 30
二、面向对象
面向对象的核心体现是将数据和处理数据的程序封装到对象中。即以对象的形式组织代码,封装数据。
2.1、面向过程
1、面向过程编程
核心是"过程"二字,过程指的是解决问题的步骤,即先干什么再干什么
基于该思想编写程序就好比在编写一条流水线,是一种机械式的思维方式
优点:复杂的问题流程化、进而简单化
缺点:可扩展性差
2.2、面向对象
2、面向对象
核心"对象"二字,对象指的是特征与技能的结合体,
基于该思想编写程序就好比在创造一个世界,你就是这个世界的上帝,是一种
上帝式的思维方式
优点:可扩展性强
缺点:编程的复杂度高于面向过程
函数与方法:都是解决问题的功能
函数:通过函数名直接调用
方法:通过附属者‘.’语法来调用
面向对象的三大特性:
封装:整合数据,数据保护
继承:子类继承父类的属性、方法
多态:继承完后对方法的重写
2.3、类和对象
类 class:对具有相同属性的对象的抽象,定义了对象的属性、方法;类其实就是一个容器(名称空间)。类体代码会在类定义阶段立即执行,并产生一个名称空间,用来将类体代码执行过程中产生的名字都丢进去。
对象 :对象是特征与技能的集合体,是类的实例化
类的声明语法:
class 类名: # class是定义类的关键字
pass
对象的产生:类的实例化
对象1 = 类名()
对象2 = 类名()
对象3 = 类名()
程序中类的用法:
1、当作名称空间从其内部取出名字来使用
2、调用类来产生对象
.:专门用来访问属性,本质操作的就是__dict__
OldboyStudent.school #等于经典类的操作OldboyStudent.__dict__['school']
OldboyStudent.school='Oldboy' #等于经典类的操作OldboyStudent.__dict__['school']='Oldboy'
OldboyStudent.x=1 #等于经典类的操作OldboyStudent.__dict__['x']=1
del OldboyStudent.x #等于经典类的操作OldboyStudent.__dict__.pop('x')
注意:
1.类中可以有任意python代码,这些代码在类定义阶段便会执行
2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过OldboyStudent.__dict__查看
3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法
4.点是访问属性的语法,类中定义的名字,都是类的属性
小结:
类拥有自己的名称空间,类的对象也拥有自己的名称空间,
所以可以通过.语法添加或使用属性和方法
定义类能不能初始设置属性与方法?
类一旦被加载(随着所属文件的加载就加载),就会进入类的内部执行类中的所有代码
python为类内置的特殊属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
class People:
identify = '人类'
def sleep(self):
print('睡觉')
print(456)
# print(People.__dict__)
p1 = People()
p2 = People()
p1,p2虽然能访问identify,但identify属于People类,只有一份
print(p1.identify)
print(p2.identify)
p1修改的并不是People的identify,相当于给自己添加一个identify属性
p1.identify = '新人类'
print(p1.__dict__, p1.identify)
p1.__dict__.clear()
print(p1.identify)
p2自己没有,还是访问的类的
print(p2.identify)
属性的访问顺序:优先加载自身的名字,如果没有再考虑类的
面向对象小案例:
class Dog:
def __init__(self,name):
self.name = name
def bulk(self):
print("%s wang wang wang!!")
d1 = Dog('peter') # 调用类产生一个对象d1,然后返回,对象会产生一个空的名称空间;触发类中__init__的执行,第一个参数默认传的就是d1.
d2 = Dog('Bob')
d3 = Dog('Henry')
d1.bulk()
d2.bulk()
d3.bulk()
2.4、对象特有的名称空间
class Student:
identify = '学生'
# Student.identify = '新学生' # 类的属性只有自己才能更改
stu1 = Student() # 实例化
print(stu1.identify) # 学生,这里自己没有,就访问类里的
stu1.identify = '学生1' # 可以给自己添加属性,不用类的
print(stu1.identify) # 学生1
print(stu1.__dict__) # 相当于给自己的名称空间添加了属性,{'identify': '学生1'}
# 类实例化后,对象的其他属性可按照如下方式添加
stu1.name = 'Bob'
stu1.sex = 'male'
stu2 = Student()
stu2.identify = '学生2'
stu2.name = 'Tom'
stu2.sex = 'female'
print(stu2.__dict__) # {'identify': '学生2', 'name': 'Tom', 'sex': 'female'}
这样添加属性显得比较冗余,而且有很多属性,因此官方为我们提供了一个初始化的方法__init__
__init__方法会在实例化对象时被调用
1、会为实例化的对象形成空的名称空间
2、就是一个方法,可以被传参,在 类名(实参) 这种方式下调用并传参 __init__(self, 形参)
3、第一个self就是要产生的当前对象
重点:在方法内部,形参拿到了实参值,利用 self.属性名=形参,对对象的名称空间添加属性
class Student:
# def __init__(self,name,sex):
# print('2>>>',self)
# self.name = name
# self.sex = sex
def fn():
print("fn run !")
def set_stu(stu,name,sex):
stu.name = name
stu.sex = sex
# print(Student.__dict__) # 存了类里函数的名字与地址
# Student.fn() # 虽然没有self,但是通过类可以直接调用
stu1 = Student()
print(stu1.__dict__) # {},set_stu并不存在stu1的名称空间,由类来调用
Student.set_stu(stu1, 'Bob', 'male')
print(stu1.__dict__) # {'name': 'Bob', 'sex': 'male'}
print(stu1.name)
print(stu1.sex)
stu2 = Student()
Student.set_stu(stu2, 'Tom', 'female')
print(stu2.name, stu2.sex)
# 如果将产生空名称空间与对名称空间赋值操作整合
class Student:
def __init__(self,name,sex):
print('2>>>',self) # 2>>> <__main__.Student object at 0x000000FA78971748>
self.name = name
self.sex = sex
def fn():
print("fn run !")
def set_stu(stu,name,sex):
stu.name = name
stu.sex = sex
stu3 = Student('Bob','male')
print('1>>',stu3) # 1>> <__main__.Student object at 0x000000FA78971748>
print(stu3.name)
print(stu3.sex)
stu3.name = 'bob'
print(stu3.name)
stu4 = Student('Tom', 'female')
print('4>>',stu4) # __init__中的self就是实例化的对象,内存地址一致
print(stu4.name)
print(stu4.sex)
2.5、类中方法的第一个默认参数
实例化与传值
class Student:
def __init__(self, name):
self.name = name
def study(self):
print(self.name + ' study')
stu = Student('Bob')
stu.study()
stu1 = Student('Tom')
stu1.study()
对象调用类的方法:
class Student:
def test(self):
print(self)
pass
stu = Student()
# 二级优化
stu.test() # <__main__.Student object at 0x00000008CDE88898>
# 一级优化
Student.test(stu) # <__main__.Student object at 0x00000008CDE88898>
# 实现原理
Student.__dict__['test'](stu) # <__main__.Student object at 0x00000008CDE88898>
类中定义的变量是类的数据属性,类可以用,对象也可以用,大家都指向同一个内存地址,类变量值一旦改变,所有对象中的也都改变。
类中定义的函数是类的函数属性,类可以用,但类来调用就是一个普通的函数,且是绑定给对象用的。
重点:方法的第一个参数一定是调用该方法的对象
2.6、类方法
重点:
1.类的属性如何定义,类的方法如何定义
2.对象的属性如何定义,对象的方法如何定义
3.什么时候定义类方法与对象方法
4.封装的语法与原理
5.封装的目的
6.对象属性的封装对外提供操作接口的手段
class Tool:
# 类自己的方法
def add(cls, n1, n2):
return n1+n2
# a如果被外界对象tool调用,那么内部调用b,b其实也是被外界对象tool调用的
def a(self):
self.b()
def b(self):
pass
res = Tool.add(Tool,10,20) # 类调用,外界传入两个参数,内部接收到两个
print(res)
# 问题:类的名字,对象都可以使用,但是出现了类与对象使用时,参数个数不一致
tool = Tool()
print(tool.add(10,20)) # 对象调用,外界传入两个参数,内部接收到三个,第一个是对象本身
class Toool:
# 类方法:可以被类与对象调用的方法,第一个参数一定是类
# 类方法不建议拿对象来调用
@classmethod
def add(cls,n1,n2):
print(id(cls))
cls.test()
return n1+n2
@classmethod
def test(cls):
pass
# 类调用
print(Toool.add(10,20))
# 对象调用
toool = Toool()
print(toool.add(100,200))
print(id(Toool),id(toool))
# 对象调用所属类的类方法,默认第一个参数传入的是 对象.__class__ 就是所属类
print(toool.__class__) # <class '__main__.Toool'>
2.7、属性与方法的总结
class OldBoy:
# 类的属性
name = '老男孩'
# 对象的属性
def __init__(self, name):
self.name = name
# 属于类的方法
# 需求:获取公司的名字
@classmethod
def get_company_name(cls):
return cls.name
# 属于对象的方法
# 需求:获取地点的名字
def get_place_name(self):
return self.name
# 先创建地点
G1 = OldBoy('上海')
G2 = OldBoy('北京')
# 类方法的使用
# 建议使用类调用
print(OldBoy.get_company_name())
# 类方法拿对象调用并没有多大的意义,不建议拿对象调用
print(G1.get_place_name())
print(G2.get_place_name())
# 对象方法的使用
# 类调用对象方法,必须把要操作的对象手动传入,不建议使用
print(OldBoy.get_place_name(G1))
print(OldBoy.get_place_name(G2))
# 对象调用对象方法,默认将自身传入,建议使用
print(G1.get_company_name())
print(G2.get_company_name())
2.8、封装
将类中的一些功能与属性,进行隐藏,不让外界直接访问(但可以间接访问)
优点:外界不能直接访问,让内部的属性与方法具有安全保障
以下代码中,类的所有属性与方法都能被外界访问,不安全
class A:
x = 20
y = 10
def f1(self):
print("f1 run")
def f2(self):
print("f2 run")
# 找到保险箱
def get_box(self):
print("找到保险箱")
self.get_money()
# 保险箱取钱操作
def get_money(self):
print('输入密码,取出100w零花钱')
a = A()
print(a.x)
print(a.y)
a.f1()
a.f2()
a.get_box()
# a.get_money()
以下代码是封装后的:
class A:
# __开头的属性,在外界不能通过 cord|__cord 直接访问:对外隐藏了
__cord = '01010101'
# __开头的方法,在外界不能通过 get_money|__get_money 直接访问:对外隐藏了
@classmethod
def __get_money(cls):
print('输入密码,取出100w零花钱')
@classmethod
def test(cls, flag):
print('test方法被外界调用')
# 在调用test与访问具体数据与功能间添加安全处理的操作
if flag == '自家人':
print(cls.__cord)
cls.__get_money()
# print(A.__cord) # type object 'A' has no attribute '__cord'
# A.__get_money() # type object 'A' has no attribute '__get_money'
A.test('自家人')
封装的原理:把用__开头的名字更名为 (_类名__变量名 ) ,所以直接用 (变量名|__变量名) 就访问不到。
如果想访问,可以通过如下访问,但是违背了我们封装的初衷
print(A._A__cord) # 01010101
A._A__get_money() # 输入密码,取出100w零花钱
2.9、对象的属性方法封装与接口提供
对象的属性与方法封装机制与类的属性与方法封装原理一样
class AAA:
def __init__(self, money):
self.__money = money
self.__id = 1000
@property # 让外界访问时像访问普通字段一样
def id(self):
return self.__id
@id.setter
def id(self, id):
self.__id = id
# 对象的属性封装
# 1、对象的属性值一般都来自外界,外界是有权再次访问的
# 2、封装的目的不是让外界无法访问,而是不让其直接访问,可以在完成安全处理后再访问
# 3.如何做到外界还是通过变量名来对属性进行取值赋值,但是是走的方法间接拿到的值
# -- __money被封装,外界还是可以通过 对象.money 取值赋值
# 取值
@property # 在外界可以 对象.money 进行取值
def money(self):
print('走方法拿到的money')
return self.__money
# 赋值
@money.setter # 在外界可以 对象.money = 新值 进行赋值
def money(self,money):
self.__money = money
# 删除
# @money.deleter
# def money(self):
# del self.__money
def get_money(self, flag):
if flag == '自家人':
return self.__money
return 0
def set_money(self, money):
self.__money += money
# 对象的方法封装一般的实现需求都是,这些方法只在内部使用
def __test(self):
print('test run')
a = AAA(88888)
print(a.get_money('自家人')) # 88888
a.set_money(1000000)
print(a.get_money('自家人')) # 1088888
# get_money和set_money可以用装饰器定义为同一个方法
print(a.money) # 1088888
a.money = 999999
print(a.money) # 999999
2.10、通过一个简单的植物大战僵尸的小游戏来消化一下
# -*- coding: utf-8 -*-
# @Author : Sunhaojie
# @Time : 2019/4/18 18:45
'''
用面向对象实现 植物大战僵尸游戏
1.定义一个僵尸Zombie类,该类可以实例化出多种僵尸对象,僵尸对象产生默认都有 名字name、血量HP、防具armor
-- 名字:普通僵尸 | 路障僵尸 | 铁桶僵尸
-- 血量:默认就是100,不需要外界提供
-- 防具:不需要外界提供,从名字中分析确定,防具的值是一个列表,从名字分析得到
-- ['无', 0] | ['路障', 5] | ['铁桶', 15] => [防具名, 防具的防御值]
-- 通过@property的getter、setter方式,对外提供防具的两个访问接口armor_name与armor_count
-- armor_name可以取值、赋值、删除值:通过一个
-- eg: 普通僵尸对象.armor_name = '铁桶',不仅改变了防具名
-- 普通僵尸对象的名字name也会变成 铁桶僵尸
-- armor_count只可以取值
2.定义一个角色User类,该类有名字name属性、以及打僵尸的beat方法
-- 名字:随意自定义
-- beat:该方法需要传入一个僵尸对象
-- 在方法内部可以实现:某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血
-- 每一次攻击,都固定扣除25滴血,但是不同的僵尸会被防具相应抵消掉一定的伤害值
-- 循环攻击僵尸,3s攻击一次,僵尸被击杀后,打印 某某用户击杀了某某个僵尸 并结束方法
3.定义一个Game类,该类有一个name属性,属性值为 "植物大战僵尸" ,该类中有一个start方法,通过Game.start()来启动游戏
-- 游戏一开始先显示游戏的名字 植物大战僵尸游戏
-- 会随机产生三种僵尸,总共产生三个作为要被击杀的对象
-- 生成一个有角色名的角色,依次去击杀随机产生的每一只僵尸
-- 开始击杀第一只僵尸 => 某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血 => 某某用户击杀了某某个僵尸 => 第一只僵尸已被击杀完毕 => 开始击杀第二只僵尸 ... => 第三只僵尸已被击杀完毕
'''
import random
import time
class Zombie:
'''
名字name、血量HP、防具armor
'''
# 匹配的字典
dic = {'普通僵尸': ['无', 0], '路障僵尸': ['路障', 5], '铁桶僵尸': ['铁桶', 15]}
def __init__(self, name, HP=100):
# 传入的僵尸类型
self.name = name
# 血量,初始100
self.HP = HP
#查僵尸的防具
@property
def armor_name(self):
return self.dic[self.name][0]
# 设置僵尸类型
@armor_name.setter
def armor_name(self, name1):
if name1 == '无':
self.name = '普通僵尸'
else:
self.name = name1 + '僵尸'
# 删除僵尸
@armor_name.deleter
def armor_name(self):
del self.name
# 取护具的防护值
@property
def armor_count(self):
return self.dic[self.name][1]
class User:
def __init__(self, name):
self.name = name
# 打僵尸
def beat(self, z):
while True:
# 每次减25点血
z.HP -= 25
# 把防具的防护值加回去
z.HP += z.armor_count
if z.HP <= 0:
print('用户[%s]击杀了[%s]' % (self.name, z.name))
break
print("用户[%s]攻击了[%s],僵尸损失[%s]血量,还剩[%s]血量" % (self.name, z.name, 25 - z.armor_count, z.HP))
time.sleep(3)
class Game:
name = "植物大战僵尸"
@classmethod
def start(self):
print(Game.name + '游戏')
zombies = []
for i in range(3):
arm = random.choice(['普通僵尸', '路障僵尸', '铁桶僵尸'])
zombies.append(arm)
u = User('||<<豌豆射手>>||')
count = 1
for n in zombies:
print("开始击杀第%s只僵尸" % count)
# 实例化僵尸类
z = Zombie(n)
# 调用击杀的方法
u.beat(z)
print("第[%s]只僵尸已被击杀完毕" % count)
count += 1
Game.start()
python基础——17(面向对象)的更多相关文章
- Python 基础 四 面向对象杂谈
Python 基础 四 面向对象杂谈 一.isinstance(obj,cls) 与issubcalss(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls ...
- python基础,函数,面向对象,模块练习
---恢复内容开始--- python基础,函数,面向对象,模块练习 1,简述python中基本数据类型中表示False的数据有哪些? # [] {} () None 0 2,位和字节的关系? # ...
- (转)Python成长之路【第九篇】:Python基础之面向对象
一.三大编程范式 正本清源一:有人说,函数式编程就是用函数编程-->错误1 编程范式即编程的方法论,标识一种编程风格 大家学习了基本的Python语法后,大家就可以写Python代码了,然后每个 ...
- 自学Python之路-Python基础+模块+面向对象+函数
自学Python之路-Python基础+模块+面向对象+函数 自学Python之路[第一回]:初识Python 1.1 自学Python1.1-简介 1.2 自学Python1.2-环境的 ...
- 二十. Python基础(20)--面向对象的基础
二十. Python基础(20)--面向对象的基础 1 ● 类/对象/实例化 类:具有相同属性.和方法的一类人/事/物 对象(实例): 具体的某一个人/事/物 实例化: 用类创建对象的过程→类名(参数 ...
- 十七. Python基础(17)--正则表达式
十七. Python基础(17)--正则表达式 1 ● 正则表达式 定义: Regular expressions are sets of symbols that you can use to cr ...
- Day7 - Python基础7 面向对象编程进阶
Python之路,Day7 - 面向对象编程进阶 本节内容: 面向对象高级语法部分 经典类vs新式类 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个 ...
- Python基础7 面向对象编程进阶
本节内容: 面向对象高级语法部分 经典类vs新式类 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 经典 ...
- Python之路,Day7 - Python基础7 面向对象
本节内容: 面向对象编程介绍 为什么要用面向对象进行开发? 面向对象的特性:封装.继承.多态 类.方法. 引子 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战> ...
随机推荐
- OS 内存泄漏 导致 整个aix主机block
问题 aix 主机 1.数据库主机使用vmstat 监控,隔几分钟 就是block 爆满. cpu 没有瓶颈,I/O 显示本地磁盘hdisk0和hdisk 1 是爆满. vmstat 同时显示大量pa ...
- GDB 格式化结构体输出
转载:http://blog.csdn.net/unix21/article/details/9991925 set print addressset print address on打开地址输出,当 ...
- 牛客网Java刷题知识点之调用线程类的start()方法和run()方法的区别
不多说,直接上干货! 前期博客 牛客网Java刷题知识点之四种不同的方式创建线程 这里很简单 首先,系统通过调用线程类的start()方法来启动一个线程,此时这个线程处于就绪状态,而非运行状态,也就意 ...
- Notepad++ 安装 Zen Coding / Emmet 插件
Zen Coding 插件 ============== 下载: Zen.Coding-Notepad++.v0.7.zip ==Installation== 1. Copy contents of ...
- 12.JAVA-基本数据类型的包装类操作
1.基本数据类型的包装类 java是一个面向对象编程语言,也就是说一切操作都要用对象的形式进行.但是有个矛盾: 基本数据类型(char,int,double等)不具备对象特性(不携带属性和方法) 这样 ...
- Django之Form组件整理
搬运自:http://www.cnblogs.com/haiyan123/p/7795771.html 一.Form类 创建Form类时,主要涉及到 [字段] 和 [插件],字段用于对用户请求数据的验 ...
- 20170308web作业1
代码20170308001: <%@ page language="java" import="java.util.*" pageEncoding=&qu ...
- PHP-PHPExcel用法详解
以下文章来源:diandian_520 http://blog.csdn.net/diandian_520/article/details/7827038 1.header header(" ...
- org.springframework.beans.factory.BeanCreationException: Could not autowire
由于我在项目中引用了如下代码,增加了 @Configurationpublic class Connection { public @Bean HttpClientConfig httpClie ...
- SQLite连接
SQLite -连接 SQLite的联接子句用于从数据库中的两个或多个表合并的记录.JOIN是用于通过使用共同的每个值从两个表结合域的装置. SQL定义了三个主要类型的连接: CROSS JOIN I ...