最新彩版:http://www.javashuo.com/article/p-purcxavn-d.htmlhtml
在线编程:https://mybinder.org/v2/gh/lotapp/BaseCode/master
在线预览:http://github.lesschina.com/python/base/oop/1.封装.html
python
此次尽可能用故事模式来说知识,上次刚说美化,此次算是第一篇了。步入正题:git
类的组成:类名、属性(没有字段)、方法github
# 类名首字母大写
class Student(object):
"""建立一个学生类"""
# 没有属性定义,直接使用便可
# 定义一个方法,方法里面必须有self(至关于C#的this)
def show(self):
print("name:%s age:%d"%(self.name,self.age))
# 实例化一个张三
zhangsan=Student()
# 给name,age属性赋值
zhangsan.name="张三"
zhangsan.age=22
# 调用show方法
zhangsan.show()
# 打印一下类和类的实例
print(Student)
print(zhangsan) #张三实例的内存地址:0x7fb6e8502d30
和静态语言不一样,Python容许对实例变量绑定任何数据 ==> 对于两个实例变量,虽然它们都是同一个类的不一样实例,但拥有的变量名称可能都不一样编程
说的比较抽象,举个例子就明了了:安全
xiaoming=Student("小明",22)
xiaoming.mmd="mmd"
print(xiaoming.mmd)
# 小明和小潘都是Student类,可是小明有的mmd属性,小潘却没有
xiaopan=Student("小潘",22)
print(xiaopan.mmd)
建立对象后,python解释器默认调用__init__方法,对必要字段进行初始化赋值app
须要注意的是:__init__并非C#中的构造函数,__new__ (后面会说) + __init__ 等价于构造函数less
第一个参数和类的其余方法同样,都是self(至关于C#里面的this,表示建立的实例自己)调用的时候直接忽略它编辑器
class Student(object):
# 初始化赋值
def __init__(self,name,age):
self.name=name
self.age=age
def show(self):
print("name:%s age:%d"%(self.name,self.age))
# 有了__init__方法,在建立实例的时候,就不能传入空的参数了
lisi=Student()
# 建立一个正确的实例
xiaowang=Student("小王",22)
xiaowang.show()
在print(类名)的时候自定义输出ide
这个有点像C#类里面重写ToString,eg:
public override string ToString()
{
return "Name:" + this.Name + " Age:" + this.Age;
}
# Python的__str__()方法
class Student(object):
def __init__(self, name, age):
self.name = name
self.age = age
# self别忘记写了,return也别忘了
def __str__(self):
return "姓名:%s,年龄:%s" % (self.name, self.age)
lisi = Student("李四", 22)
print(lisi) #如今打印就是你DIV的输出了
C#、Java里面都是有访问修饰符的,Python呢?
Python规定,若是以双下划线__开头的属性或者方法就是私有的
变量名相似xxx的,也就是以双下划线开头,而且以双下划线结尾的,是特殊变量。特殊变量是能够直接访问的,不是private变量
在说私有属性前,咱们来个案例说说属性不私有的弊端,eg:
小明同窗学了点C#,而后学习了上面的知识,心想 ~ Python这么搞安全性呢?不行,我得构造构造,因而有了下面的代码:
class Student(object):
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
return self.name
def set_name(self, name):
self.name = name
def get_age(self):
return self.age
def set_age(self, age):
if age > 0:
self.age = age
else:
print("age must > 0")
def show(self):
print("name:%s,age:%d" % (self.name, self.age))
小明心想,想要修改age属性,你经过set_age我就能够判断了哇,仍是本宝宝聪明
这时候小潘过来了,淡淡的一笑,看我怎么破了你 ~ 看代码:
zhangsan = Student("张三", -20)
zhangsan.show() # name:张三,age:-20
zhangsan.age = -1 # set_age方法形同虚设,我彻底能够直接访问字段了
zhangsan.show() # name:张三,age:-1
小潘傲气的说道~大叔,给你脸呢。我就是不去访问你设定的方法怎么滴呢?
小明急的啊,赶忙去找伟哥求经。不一会,傲气的贴出本身的New Code,心想着我私有属性都用上了还怕个毛毛:
class Student(object):
def __init__(self, name, age):
self.__name = name
# 通常须要用到的属性都直接放在__init__里面了
# self.__age = age
self.set_age(age)
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
def get_age(self):
return self.__age
def set_age(self, age):
if age > 0:
self.__age = age
else:
print("age must > 0")
def show(self):
print("name:%s,age:%s" % (self.__name, self.__age))
小潘冷笑道~呵呵,而后使用了上次的绝招:
zhangsan = Student("张三", -20)
zhangsan.__age = -1 # 一样的代码,只是属性前面加了下划线
zhangsan.show()
此次小潘同志傻眼了,彻底不能访问了啊?不行,怎么能被小明大叔笑话呢?
因而上网翻资料,国内不行就国外,外文很差就翻译,终于找到一个新破解方式:
双下划线开头的实例变量不能直接访问,是由于Python解释器对外把__age变量改为了_Studentage,因此,仍然能够经过**_Studentage**来访问:
# 搞事情
zhangsan._Student__age = -1
zhangsan.show()
建议你不要这么干,不一样版本的Python解释器可能会把__age改为不一样的变量名
有些时候,你会看到以一个下划线开头的实例变量名,好比_age这样的实例变量,外部是能够访问的。
可是,请把它视为私有变量,不要随意访问(Python不少东西全凭自觉~捂脸@_@)
小潘终于长叹一口气,而后还不忘取笑小明同窗~你这属性搞的,真麻烦,老是经过方法调用,太累了 <_> 鄙视!
这可把小明急的啊,学习的积极性都没有了,吃了碗牛肉面就去伟哥那边好好取经了~
# 私有方法一笔带过
class Student(object):
"""私有方法"""
def __go_home(self):
pass
zhangsan = Student()
zhangsan.__go_home() # 访问不到
Python内置的@property
装饰器就是负责把一个方法变成属性调用的,来个例子
class Student(object):
def __init__(self, name, age):
# 通常须要用到的属性都直接放在__init__里面了
self.name = name
self.age = age
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if age > 0:
self.__age = age
else:
print("age must > 0")
def show(self):
print("name:%s,age:%s" % (self.name, self.age))
xiaoming = Student("小明", 22)
xiaoming.name = "小潘"
xiaoming.age = -2
xiaoming.show()
把一个getter方法变成属性,只须要加上@property
就能够了
@方法名.setter
,负责把一个setter方法变成属性赋值
固然了,若是只想读 ==> 就只打上@property
标签
小明同窗高兴坏了,赶忙大吃了一顿~
建立对象后,python解释器默认调用__init__() 方法
当删除一个对象时,python解释器也会默认调用__del__() 方法(有点析构函数的味道)
当有1个变量保存了对象的引用时,此对象的引用计数就会加1
当使用del删除变量指向的对象时,若是对象的引用计数不为1,那么每次删除计数减1,当计数为1的时候再调del就真把对象删了
这个能够结合我以前说过的连接来理解:于连接文件的探讨
看着老师夸夸其谈,小明愣了楞,摸摸肚子想到,真BB,我先搞个例子练练:
# 定义一个临时类
class Temp(object):
def __del__(self):
print("你被干掉了")
验证方面用编辑器比较合适,交互模式下可能不是真正的结果
# 对象被s1和s2引用
s1 = Temp()
s2 = s1
del s1 # 只删除s1,新建立的对象并无被删除
print("-" * 10)
输出:(最后的被干掉是程序退出了)
# ---------- # 你被干掉了
若是用连接来解释就是这样的:
此次两个都删掉:
t1 = Temp()
t2 = t1
del t1
del t2
print("-" * 10)
输出:
# 你被干掉了 # ----------
都删了,天然就真删掉了
这样搞比较麻烦,咱们引入一下获取引用个数:getrefcount(object也会占1个引用计数)来个案例:
# 程序退出的时候,在他运行期间全部占用资源归还操做系统
# 引用计数
import sys
t1 = Temp()
print(sys.getrefcount(t1)) #(结果比实际引用大1)【object也会占1个引用计数】
t2 = t1
print(sys.getrefcount(t1))
print(sys.getrefcount(t2))
del t1
print(sys.getrefcount(t2))
# sys.getrefcount(t1)#被删掉天然没有了
del t2
print("-" * 10)
运行结果:
2 3 3 2 你被干掉了 ----------
我再贴一种状况,你能够思考下为啥:
t1 = Temp()
t2 = Temp()
del t1
del t2
print("-" * 10)
输出:
# 你被干掉了 # 你被干掉了 # ----------
小潘扭过头瞅了一眼说道:“大叔,你__new__忘记写案例了”
小明一愣神,立马反应过来讲:“我这叫谋然后动~”
当你实例化一个对象的时候,就会执行new 方法里面的方法。new方法在类定义中不是必须写的,若是没定义,默认会调用object.new去建立一个对象
__new__方法中至少要有一个参数cls,表明要实例化的类,此参数在实例化时由Python解释器自动提供
__new__方法中必需要有返回值(返回实例化出来的实例)
小明翻阅了官方文档,淡定的打下了以下标准格式的代码:
class Dog(object):
def __init__(self, name):
self.name = name
print("初始化完毕")
def __str__(self):
return "Dog的名字叫:%s" % self.name
def __new__(cls, name):
# 注意参数,是cls,而后其余参数和init保持一致便可
print("建立对象完毕")
# 别忘记写返回值哦
return object.__new__(cls)
def main():
happy = Dog("Happy")
print(happy)
if __name__ == '__main__':
main()
关于__name__在模块调用的时候会详细说,你能够先这样理解:若是直接运行py文件就执行,别人调用那么你的main就不执行了
标准写法:
# 1.导入的模块
# 2.class的定义
# 3.其余方法定义
def main():
pass
if __name__ == '__main__':
main()
其余内容后面会继续说,封装部分再说说静态方法和类方法之类的就结束了(和C#仍是有很大区别的)
小明问老师:“老师老师,怎么没有静态类,静态属性之类的东西呢?”
老师笑而不语道:“小家伙原来不只仅是体重增长啊,这求知欲也是大大的增长呢 ~ 且听我慢慢道来”
类在程序里面也是对象(你姑且能够认为全部的类都相似于C#里面的静态类),而经过类实例化的对象,叫实例化对象
实例属性 --> 实例对象相互之间不共享 通常咱们都是在__init__
中定义
类属性(相似于C#里面的静态字段) --> 属于类对象,多个实例对象之间共享
注意一下:相同名称的实例属性将屏蔽掉类属性(尽可能别同名)
类属性除了能够经过 类名.类属性 访问外,还能够直接 实例对象.类属性 (C#中抽象类和静态类是不能被实例化的)
来个案例更直观点:
class Person(object):
# age为类属性
age = 1
def __init__(self, name):
# name为实例属性
self.name = name
def main():
# 类名.类属性
print(Person.age)
xiaoming = Person("小明")
# 对象.类属性
print(xiaoming.age)
if __name__ == '__main__':
main()
若是须要在类外 修改类属性,必须经过类对象去引用而后进行修改
若是经过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性
若是经过实例对象去引用该名称的属性,实例属性会强制 屏蔽掉类属性,即引用的是实例属性,除非del了该实例属性才能正常访问类属性
你能够理解为,Python这么作只是为了方便你获取,该怎么修改还得怎么作。来看个案例:
class Person(object):
# age为类属性
age = 1
def __init__(self, name):
# name为实例属性
self.name = name
def main():
# 类名.类属性
print(Person.age)
# 经过对象.类属性修改
xiaoming = Person("小明")
xiaoming.age = 100
print(xiaoming.age) # 其实,并无修改为功,只是产生了一个同名age
print(Person.age) # 对吧,类属性并无被修改
# 经过类名修改
Person.age = 22 # 若是须要在类外修改类属性,必须经过类对象去引用而后进行修改
print(xiaoming.age) # 刚才已经建立一个同名age,因此如今显示的是刚才的值
print(Person.age) # 经过类名.类属性 就能够看到值被修改了
# 若是你仍是不信,能够建立一个新对象看看
xiaopan = Person("小潘")
print(xiaopan.age)
# xiaoming实例对象想访问怎么办?
# 除非del了该实例属性才能正常访问类属性
del xiaoming.age
print(xiaoming.age) # 这时候访问的就是 类属性 了
if __name__ == '__main__':
main()
先说说 实例方法,实例方法第一个定义的参数只能是实例自己引用self
,只能经过实例调用(就是咱们以前用的 def func_name(self,xxx):
)
类方法:是类对象所拥有的方法,须要用修饰器@classmethod
来标识,第一个参数必须是类对象cls
,能够经过类或者实例直用
静态方法:定义静态方法使用装饰器@staticmethod
,没有默认的必须参数,经过类和实例直接调用
静态方法中不须要额外定义参数,所以在静态方法中引用类属性的话,必须经过 类对象来引用(访问)
小明眼花缭乱的对老师说道,老师给我看几个案例吧:
class Dog(object):
# 类属性
name = "小汪"
# 实例方法
def __init__(self, age):
# 实例属性
self.age = age
# 打印看看
print("self id:%s" % id(self))
# 类方法
@classmethod
def show_name(cls):
# 访问类属性 cls.xxx
print("我叫%s" % cls.name)
# 打印看看
print("cls id:%s" % id(cls))
# 静态方法
@staticmethod
def say_hello():
print("汪汪汪")
def main():
# 类名方式访问
Dog.show_name()
Dog.say_hello() # 类名的方式能够访问静态方法
# 实例对象方式访问
dog = Dog(2)
dog.show_name()
dog.say_hello()
if __name__ == '__main__':
main()
通常都是这样用的(供参考):
实例方法:通常平时用的都是它
类方法:类方法用在模拟C#多个构造函数(Python里面不能有同名函数) or 你须要 对类属性、类方法操做之类的
静态方法:通常 都是独立功能,相似于函数,只不过在面向对象里面通常这么用
C#面向对象比较优美,来个封装的案例基本上就搞定了:
using System;
namespace _1Encapsulation
{
public class Student
{
/// <summary>
/// 字段
/// </summary>
private int _age;
/// <summary>
/// 属性
/// </summary>
public int Age
{
get
{
return _age;
}
set
{
if (value > 1)
{
_age = value;
}
}
}
/// <summary>
/// 自动化属性
/// </summary>
public string Name { get; set; }
/// <summary>
/// 自动属性必需要有get访问器
/// </summary>
public string SNum { get; }
private int _gender;
public int Gender
{
set
{
_gender = value;
}
}
/// <summary>
/// 构造函数的名字必须与类名一致
/// 构造函数没有返回值也没有viod
/// 默认自动生成一个无参构造函数,当有一个有参构造函数的时候无参构造函数便不会自动建立
/// </summary>
public Student() { }
/// <summary>
/// 有参构造函数
/// </summary>
/// <param name="name"></param>
/// <param name="age"></param>
public Student(string name, int age)
{
this.Name = name;
this.Age = age;
}
/// <summary>
/// this调用当前类的某个有参构造函数
/// </summary>
/// <param name="name"></param>
/// <param name="age"></param>
/// <param name="gender"></param>
public Student(string name, int age, int gender) : this(name, age)
{
this.Gender = gender;
}
/// <summary>
/// 某个方法
/// </summary>
public void Show()
{
Console.WriteLine("Name:" + this.Name + " Age:" + this.Age + "\n");
}
public override string ToString()
{
return "Name:" + this.Name + " Age:" + this.Age;
}
}
}
调用部分:
using System;
namespace _1Encapsulation
{
class Program
{
static void Main(string[] args)
{
Student s = new Student() { Name = "mmd", Age = 13, Gender = 1 };
s.Show();
Student s1 = new Student("dmm", 20);
s1.Show();
Console.WriteLine(s);
}
}
}