之前我们已经知道了面向对象的概念及在python中创建空类,带方法的类,带初始化方法的类,带实例化方法的类并认识了类的成员等。在我们将其与Java代码进行对比后,确实发现了python有它的方便之处。面向对象的内容不止之前文章中提到的那些,我们还得在面向对象这个奇妙的国度继续探索和遨游。~·~~~~~~~·~
现在我们接着进一步认识python中的面向对象。
目录
一,私有成员
我们创建的类成员默认都是公有成员,可以在类的外部通过类或对象随意访问,这样显然不够安全,因为什么类都可以来访问我们的成员,那么它们就极有可能在外部就可以改变我们的成员,并修改里面的数据,所以为了我们数据的安全性,在创建类的时候都会定义一些私有的类成员来存储不想对外公开的数据,在一定程度上限制在类的外部对类成员的访问。在Java中的私有化成员都是使用private,protect等关键字进行修饰的,但是在python中,它是通过在类成员的名称前面添加双下划线__的方式来表示该成员为私有成员。
接下来我们使用具体的代码来看一下私有成员的定义和使用及效果(与公有成员进行对比):
1,对比公有属性和私有属性的定义和访问:
现在我们先声明两个类属性,并给公有的类属性name赋一个初始化的值, “BigData”,以及一个私有化的类属性__age,并给一个初始化的值24。
class TestClass:
name="BigData" # 公有类属性
__age=24 # 私有类属性
object1=TestClass()
print(object1.name) # 对象访问公有属性
print(object1.age) # 对象访问私有属性
2,对比公有方法和私有方法的定义和访问
如下,我们在类里面声明两个实例方法,两个实例方法最根本的区别在于一个是公有的say(),另外一个是私有的__secret()。
class TestClass:
def say(self): # 公有实例方法
print("hello")
def __secret(self): # 私有实例方法
print("有内鬼,停止交易")
object1=TestClass() # 创建对象
object1.say() # 对象调用公有实例方法
object1.secret() # 对象调用私有实例方法
3,使用公有方法访问私有成员
从上面我们可以看到,如果我们在类的外面创建了一个对象来调用我们的私有化的类成员的话都会显示没有这个属性,这样子保证了我们的一些数据的安全性,就不是随随便便一个对象就能够进行访问的。但是如果如果我们想要去调用的话又该怎么去编写我们的代码呢?
之前我一直以为书上的概念:私有属性和方法只能在类中访问。是直接在类中打印或者是输出该属性,结果并不是,而是需要在含有私有化属性或者是方法的类体中创建一个公有的方法(该公有方法可以调用我们的私有属性和方法),然后在类的外部创建一个对象,该对象可以来调用包含有私有属性和方法的公有方法,来实现打印和输出我们的私有属性和方法的功能,如下:
class TestClass:
__age=24
def say(self): # 公有实例方法
print("hello")
def __secret(self): # 私有实例方法
print("有内鬼,停止交易")
def public_contain(self):
print(self.__age) # 访问私有类属性
self.__secret() # 调用私有实例方法
object1=TestClass() # 创建对象
object1.public_contain() # 调用包含有私有成员的公有方法
如上,我们可以看到,成功访问到了私有属性和调用了私有方法。
二,封装
封装和我们之前认识的定义私有成员的作用有点类似,都是为了保证类内部的数据的安全。但是我们的封装并不是值属性或者是方法的私有化,而是在编写初始化方法(构造方法)的时候直接将我们的属性声明为私有属性,并添加2个可供类外部调用的公有方法,分别用于设置或获取私有属性的值,多想不如多敲代码,现在我们使用代码来看一下具体的实现和效果:
class TestClass:
def __init__(self,name): # 构造方法
self.__age=24 # 私有属性age,默认为24
self.name=name # name变量属于调用该方法的对象
def set_age(self,age): # 设置私有属性值的方法
if(age<0):
print("您的年龄不合法")
else:
self.__age=age # 如果年龄合法就想该年龄赋值给调用的对象
def get_age(self): # 获取私有属性的方法
return self.__age
object1=TestClass("张三")
object1.set_age(20) # 对象调用设置私有属性的方法来修改私有属性
print("%s的年龄为%s" % (object1.name,object1.get_age()))
当我们输入的年龄不合法(年龄为负数)时,就不会将对象修改的内容添加进去,而是打印输出提示:“您的年龄不合法”,并使用类属性默认的age属性值24.
从开始到现在,不知道大家有没有发现我们的对象实例化及打印输出对象并没有写在main方法中,但是却可以实例化成功我们的对象及打印输出出我们想要的内容,这是为什么呢?究其原因主要是因为我们的python它是个脚本语言,在运行程序的时候它自己会启动一个线程,也不一定需要main方法的存在,它就可以在文件中进行对象的实例化,方法调用等操作。
但是实际上这种方式并不规范,我们的文件都需要有一个main方法入口,最为主函数的形式存在,不管是对象的实例化,方法的调用,还是线程的开启,都需要在main方法中书写,咱写代码要规范地去编写,还是在main方法中比较好。
我们修改代码后如下所示:
接下来我们去认识面向对象中的另外一个概念:继承
三,继承
1,单继承
1)python中实现单继承
在支持面向对象编程思想的计算机语言中,继承是一个很不错的东西,它支持我们代码的复用(重用),减少了我们对同一个功能的代码进行重复编写,费时费力。并且继承还可以分为单继承和多继承【根据子类继承父类的多少进行划分:如果继承了一个父类则为单继承,两个或者是两个以上则为多继承】,现在我们先来认识python中的单继承,在python中的继承不像Java中那样,需要写上关键字extends,我们只需要在类名后面带上一个小括号并在里面写上我们的父类即可,如下:
现在我们将使用这个继承了TestClass的子类来创建一个对象,并调用父类重写的toString方法:
class TestClass:
def __init__(self, name): # 构造方法
self.name = name # name变量属于调用该方法的对象
__age = 24 # 私有化的实例属性
def set_age(self, age): # 设置私有属性值的方法
if (age < 0):
print("您的年龄不合法")
else:
self.__age = age # 如果年龄合法就想该年龄赋值给调用的对象
def get_age(self): # 获取私有属性的方法
return self.__age
def __str__(self): # 重写toString方法
return f"TestClass [name=' {self.name} ', age={self.__age}]"
class SubClass(TestClass): # SubClass类继承TestClass类
pass # 因为继承了一个父类,所以类体里面不需要写上具体的类成员
if __name__ == '__main__':
sub_object1 = SubClass("子类一号") # 使用空类(已经继承了TestClass类)创建对象
sub_object1.set_age(99)
print(sub_object1.__str__())
如上我们可以看到即使一个空类里面什么都没有,但是它可以通过继承机制去继承一个父类(基类/超类)来获得类成员并实现相同的功能,而不需要再去写相同的代码。 那么在Java中又该如何去体现继承呢?
2)java中实现单继承
如果我们要到Java中去实现类之间的继承的话,就需要使用到extends关键字,并如下编写代码:
public class ExtendDemo1 {
String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public ExtendDemo1(){ } // 无参构造方法
@Override
public String toString() {
return "ExtendDemo1 [name=" + name + ", age=" + this.getAge() + "]";
}
public static void main(String[] args) {
Sub_ExtendDemo1 sub_object01=new Sub_ExtendDemo1(); // 使用父类的无参构造方法创建对象
sub_object01.name="子类一号"; // 从父类那继承来的name属性
sub_object01.setAge(100);
System.out.println(sub_object01.toString());
}
}
// 子类(派生类)
class Sub_ExtendDemo1 extends ExtendDemo1{ } // 继承了ExtendDemo1的空类
我们可以看到python的继承和Java的继承机制都是大同小异的,创建的子类都可以去调用父类的方法或者是访问父类的属性(公有的)。
2,多继承
多继承的案例在我们的生活中也有很多,比方说车子+房子=房车,电脑+手机=平板,椅子+床=躺椅,驴+马=骡等。
毫无疑问的是,根据前两者事物结合而成的事物更加优良。我们这里就拿躺椅来作为例子进行多继承案例分析:
1,椅子是拿来坐的,不能实现睡觉的功能
2,床只能在家里面睡,并不能放到办公室来睡(首先空间不足,其次就是还得需要整理),因此也没有椅子可以拿来放在办公室坐的功能。
3,于是就有富有创造性的人想到了躺椅,既有椅子可以坐的功能,又有像床一样可以躺着休息睡觉的功能。
现在我们编写代码来表示这些内容。
1)python中实现多继承
python中的多继承可以像之前实现单继承那样,直接往我们的类名后面的括号里面传入子类要继承的父类(基类/超类)即可,只不过这一次我们需要传入的是多个父类,如下:
class Chair(object): # 基类
def cansit(self):
print("可以用来坐")
class Bed(object): # 基类
def cansleep(self):
print("可以用来躺")
class DeckChair(Chair,Bed): # 继承了两个基类的子类
pass # 空类
if __name__ == '__main__':
deck_chair=DeckChair() # 创建躺椅对象
deck_chair.cansit() # 调用椅子可以坐的功能
deck_chair.cansleep() # 调用床可以躺的功能
如上,我们可以看到,躺椅同时具备了椅子和床的功能。但是有些时候椅子和床的功能会有一样的地方即存在同名的功能,我们可以思考一下,这个时候我们的躺椅会先调用哪个父类的同名的功能呢?
答案是:子类先继承哪个类,就会先调用哪个类的方法。例如我们的躺椅先继承的是椅子,如果椅子里面存在一个方法soft()和床类中的soft()方法同名,那么躺椅就会只调用椅子中的soft方法:
class Chair(object): # 基类
def cansit(self):
print("可以用来坐")
def soft(self):
print("椅子是软底的")
class Bed(object): # 基类
def cansleep(self):
print("可以用来躺")
def soft(self):
print("床是软底的")
class DeckChair(Chair,Bed): # 继承了两个基类的子类
pass # 空类
if __name__ == '__main__':
deck_chair=DeckChair() # 创建躺椅对象
deck_chair.cansit() # 调用椅子可以坐的功能
deck_chair.cansleep() # 调用床可以躺的功能
deck_chair.soft() # 调用父类中同名的方法
如上我们可以看到,调用的是Chair类(子类先继承的类)中的soft方法。通过如上代码,我们可以知道,在python中的多继承只需要在单继承的基础上多传入了几个类而已,但是另外另外一个使用了面向对象编程思想的计算机语言:Java,它来实现多线程就不是那么简单的事情了。
接下来我们使用Java语言,跟python进行对比,看一下它们之间实现多继承的区别。
2)Java中实现多继承
在Java中并不存在多继承的概念,我们只能通过实现接口的形式来实现多继承的功能。如下:
文件:
I_Chair.java
public interface I_Chair { // 椅子类接口
public abstract void cansit(); // 抽象方法
}
I_Bed.java
public interface I_Bed { // 创建床类接口
public abstract void cansleep(); // 抽象方法
}
DeckChair.java
public class DeckChair implements I_Chair,I_Bed { // 创建躺椅类并让其实现椅子接口和床类接口
@Override
public void cansleep() { // 重写床接口的抽象方法
// TODO Auto-generated method stub
System.out.println("可以用来躺");
}
@Override
public void cansit() { // 重写椅子接口的抽象方法
// TODO Auto-generated method stub
System.out.println("可以用来坐");
}
public static void main(String[] args) {
DeckChair dc=new DeckChair(); // 创建躺椅对象
dc.cansit(); // 调用重写的椅子接口的抽象方法
dc.cansleep(); // 调用重写的床接口的抽象方法
}
}
如上,我们可以看到,在Java中需要使用先定义两个接口,之后再去创建一个类来实现它们。
以上就是继承的简单了解,因为在python中的使用到继承的地方也就是这些东西,知道怎么去让一个类继承另外一个类,并且能够使用父类(超类/基类)的类成员,以及能够通过父类提供的公有方法去访问私有成员就行,如果以后我遇到了更加复杂的继承相关的使用的话,会再进一步的补充该博客的内容。
四,多态
多态是面向对象中重要的特性之一。之前我们也知道了python在使用继承的概念,子类去调用父类同名的方法时,只会去调用首先继承的父类的同名方法,但是如果我们也想要去调用另外一个父类的同名方法时该怎么办?在我们的python中就只需要去定义(或创建)一个和父类同名的方法作为一个接口方法(该方法只有一个参数:对象类型),之后我们再在方法体里面使用传入的对象去调用它对应的同名方法即可。
在python中的多态,它可以实现两个类(或者是不同类)的同一个功能(或者是同一个方法即同名方法)通过同一个接口调用,并表现出不同的行为。如下我们通过代码的形式来看一下具体的实现:
class Chair(object): # 基类
def cansit(self):
print("可以用来坐")
def soft(self):
print("椅子是软底的")
class Bed(object): # 基类
def cansleep(self):
print("可以用来躺")
def soft(self):
print("床是软底的")
class DeckChair(Chair,Bed): # 继承了两个基类的子类
pass # 空类
if __name__ == '__main__':
deck_chair=DeckChair() # 创建躺椅对象
def soft(obj): # 通过这个接口调用父类中同名的方法
obj.soft()
soft(deck_chair) # 通过同一个接口调用了soft方法
soft(deck_chair)
如上,我们可以看到现在已经成功的调用了另外一个父类的同名方法。
接下来的内容为Java有关的,不感兴趣的可以直接结束阅读该文章,因为后面没有python有关的知识。
相信大家依旧记得Java语言也是面向对象的语言,因此它也有多态的概念,虽然它们的逻辑都差不多,就是同一个方法,表现出了不同的行为(功能),但是在Java中实现多态和python极其不同。
在Java中的多态不需要去实现很多个类,但是得去继承一个父类(基类,超类)。然后Java中的多态体现在可以去重写父类中的方法,并变现出不同的行为,如下:
public class CatDemo {
public static void jiaosheng(){
System.out.println("猫叫声");
}
public static void main(String[] args) {
CatDemo cat1=new CatDemo(); // 使用父类类型创建的对象
CatClass cat2=new CatClass(); // 使用子类类型创建的对象
cat1.jiaosheng(); // 父类类型调用
cat2.jiaosheng(); // 子类类型调用
}
}
class CatClass extends CatDemo{ // 创建一个CatClas去继承CatDemo类
public static void jiaosheng(){ // 重写父类方法
System.out.println("喵~");
}
}
Java中的多态不止上面这点内容,如果有兴趣的可以去查看相关知识。
python的面向对象就到这里结束了,我们通过Java和python中面向对象思想的实现 进行对比来了解不同的计算机语言的面向对象的不同实现来让我们加深对这个面向对象思想的认识与了解。
有问题的请在评论区留言。