您的位置:首页 > 编程语言 > Python开发

python和php的面向对象

2015-11-14 18:03 681 查看
python和php的面向对象
OOP术语的定义
类:对具有相同数据和方法的一组对象的描述或定义。
对象:对象是一个类的实例。
实例(instance):一个对象的实例化实现。
标识(identity):每个对象的实例都需要一个可以唯一标识这个实例的标记。
实例属性(instance attribute):一个对象就是一组属性的集合。
实例方法(instance method):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。
类属性(classattribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化
类方法(classmethod):那些无须特定的对性实例就能够工作的从属于类的函数。
关注一个语言的OPP,主要关注:
1、元素概念
2、各元素在类内(实例符、类符)外(类名)和其它域引用方法及特性(在哪定义、在哪用、是否可以定义静态属性)
3、类特性:构造、析构、权限控制(封装性)、继承、多态性(覆盖与重载)、魔术方法、自省与反射、接口与抽象类

Python的面向对象
关键元素(成员方法 成员变量)
Python中定义类的方式比较简单:
class 类名
类变量
def __init__(self,paramers):
def 函数(self,...)
……

在Python类中定义的成员方法通常有三种
实例方法,类方法以及静态方法。实例方法一般都以self作为第一个参数,必须和具体的对象实例进行绑定才能访问,类方法以cls作为第一个参数,cls表示类本身,定义时使用@classmethod
静态方法不需要默认的任何参数,跟一般的普通函数类似.定义的时候使用@staticmethod。

因为各个方法传入的参数不一样,这样定义的权限就更加清晰,比如说类方法传入cls,自然不能定义和访问实例变量self.x。

成员变量包括类变量和实例变量而在类的方法中定义的变量叫实例变量。

关键元素有以下几种:

类变量(所有方法外、类方法cls.x)
实例变量(方法中self.x)不加self则只是该方法内部局部变量。有了self就有了全局属性,在类的内
部和外部都可以访问了

实例方法
类方法
静态方法

其中类变量和类方法、静态方法属于静态属性(任何一个对象的销毁都不会释放,直到进程退出)。不经实例化就可以使用。会影响所属类的所有对象

实例化与元素引用

new一个实例就是在类名后面加()即可 obj = class_name()
引用符号只有一个.
self 在类的内部表示实例本身。cls表示类本身。
成员四个引用源:类名、cls、实例名、self、
前两者只能引用静态成员

类变量的定义与引用

类变量在所有的方法外面 x= ,也可以在类的内部任何可以引用它的地方定义,甚至在类的外部也可以。一经定义,就是类的属性。使用前注意一定要提前定义。为了维护方便,尽量只在所有方法的外面初始化,杜绝外部赋值。

1、在类的内部直接使用类名.类变量的方式引用。
2、在类的内部如果没有同名的实例变量,在实例方法中,self.x也可以搜索到。但如果改动的话,或者有同名的实例变量的话则会覆盖类变量。所以最好是self.__class__.x
3、在类的内部,类方法中可以使用cls.x引用。
4、在类的外部,类名.类变量引用,实例.__class__.x引用,在没有同名实例变量的时候也可以
使用实例.x引用

类变量的改动影响当前进程中所有文件所使用的该类和类的实例。

class A:
a1 = 1
def echo(self):
A.a1 += 1
def echo2(self):
self.a1 += 1 (不建议这样使用,相当于搜索类变量,然后初始化一个实例变量,这样即使
是类变量受影响变了,也通过实例索引不到了)
def echo3(self):
self.__class__.a1 += 1
@classmethod
def echo4(cls):
print cls.a1

obj = A()
obj.echo()

print obj.a1
print A.a1

obj.echo2()

print obj.a1
print A.a1

obj.echo3()

print obj.a1
print A.a1

2
2

3
2

3
3

实例变量的定义和引用

实例变量定义在实例方法中,即入参带有self的方法。甚至是类的外部使用实例.定义(避免这样用)。self.x 一经定义,则是类的属性。可以在任何实例方法中定义,使用前一定要定义过。在外部实例化后才能生效。

1、在类的内部,实例方法中使用self.x定义和引用
2、在类的外部,实例.x引用

实例变量的变动只影响当前实例

class A:
def __init__(self,a):
self.name = a
def echo(self):
self.name = "regou"
self.name2 = 'juejiang'
def echo2(self):
if self.name2 == 'juejiang':
print 'here'

obj = A('wangwei')
print obj.name
#print obj.name2 报错
obj.echo()
print obj.name
print obj.name2
obj.echo2()

实例方法的引用

实例方法以def func(self) 定义在类中。在内部实例化方法中可以使用。在外部只有实例化以后才能调用。

1、在类的内部,实例方法中使用self.x引用
2、在类的外部,实例.x引用

class A:
def __init__(self,a):
self.name = a
def echo(self):
self.name = "regou"
def echo2(self):
self.echo()
@staticmethod
def echo3():
self.echo()

a = A('wangwei')
#a.echo3() 报错
a.echo2()
print a.name

静态方法和类方法的引用

静态方法冠以@staticmethod定义在类中。不经过实例化就可以使用,不传入cls也没有self,与函数没有区别,只是放在了类的内部。方便归类以及与类的其他功能配合使用。因为没有传self,不用担心它会调用实例化后才能使用的东西。

1、在类的内部,使用类名.静态方法()引用
2、在类的内部,实例方法中,使用self.__classs.静态方法()引用 或者self.静态方法()
3、在类的内部,类方法中,使用cls.引用
4、在类的外部,类名.静态方法引用,实例.__class__.x引用,也可以实例.x引用

class A:
test_var = 99
def echo(self):
A.echo2()
self.__class__.echo2()
self.echo2()
@staticmethod
def echo2():
print A.test_var
@classmethod
def echo3(cls):
cls.echo2()
print cls.test_var

obj = A()
obj.echo()
obj.echo2()
obj.__class__.echo2()
A.echo2()
A.echo3()

访问控制
Python的访问控制相对简单,没有public(内外 可继承),private(类内 不可继承),protected(类内 可继承)等属性,python认为用户在访问对象的属性的时候是明确自己在做什么的,因此认为私有数据不是必须的,但是如果你必须实现数据隐藏即私有数据,也是可以的。事实上,即使是private的,Python也不能阻止你从外部访问它对Python来说,似乎约定(convention)更重要些,大部分的规则都是通过约定来表达,而不是从机制(mechanism)上去保证。

构造与析构

惨绝人寰,如果在子类中不显示的调用父类的构造函数,则不会自动执行父类的

class A:
def __init__(self):
print "create a"

class B(A):
def __init__(self):
print "create b"
obj = B()

只会打印create b

class A:
def __init__(self):
print "create a"

class B(A):
def __init__(self):
print "create b"
A.__init__(self)
obj = B()

关于构造函数,在继承中有几条法则:
1、如果子类没有定义自己的构造函数,父类的构造函数会被默认调用,但是此时如果要实例化子类的对象,则只能传入父类的构造函数对应的参数,否则会出错

2、如果子类定义了自己的构造函数,而没有显示调用父类的构造函数,则父类的属性不会被初始化,显示调用父类,子类和父类的属性都会被初始化

Python由于具有垃圾回收机制,通常不需要用户显示的去调用析构函数(这话说的 ,构造和析构本来就不用显示调用才对),即使调用,实例也不会立即释放,而是到该实例对象所有的引用都被清除掉后才会执行。

>>>class P:
def__del__(self):
print"deleted"

>>>class S(P):
def__init__(self):
print'initialized'
def__del__(self):
P.__del__(self)
print"child deleted"

>>>a=S()
initialized
>>>b=a
>>>c=a
>>>id(a),id(b),id(c)
(18765704,18765704, 18765704)
>>>del a
>>>del b
>>>del c
deleted
childdeleted
>>>

继承、多态性(覆盖与重载)

Python同时支持单继承与多继承,继承的基本语法为class新类名(父类1,父类2,..),当只有一个父类时为单继承,当存在多个父类时为多继承。子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的变量和方法。在传统类中,在多继承中如果子类和父类中同名的方法或者属性,在查找的时候基本遵循自左到右,深度优先的原则。
多态性:根据引用相同的对象,但表现不同。多态存在两种形式,覆盖和重载。所谓的覆盖就是在子类中重写父类的方法。函数重载是指一个标示符用作多个函数名,且能通过函数参数的个数或参数类型区分开来。(多个函数的名称以及返回值类型均相同,仅参数类型或参数个数不同)
python没提供函数重载的方法,因为:
为了考虑为什么 python 不提供函数重载,首先我们要研究为什么需要提供函数重载。函数重载主要是为了解决两个问题。1。可变参数类型。2。可变参数个数。另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为
python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。

静态语言需要 function overloading 主要是为了解决调用灵活性的问题,Python 这么灵活的脚本语言用 function overloading 纯属多此一举

个人以为,一个语言选择重载或者可选参数两者中的一种就是了

自省与反射

两个概念认为是相似的。用于实现在运行时获取未知对象的信息。面向对象的编程中对象被赋予了自省的能力,而这个自省的过程就是反射.通过使用反射api就能够实现动态的获 取一个类所有属性和方法以及调用该方法和属性. 自省是“道”,反射是“术”。

比如说我想动态调用一个类的方法,或者我想知道我的一个类到底有哪些方法,有哪些属性。这就叫做反射。比如有时候你想知道类的方法的是private还是public,只能靠反射了。你想知道某个函数的注释是什么,只能靠反射了。简单的说就是能让你进入类,对象,函数等语言元素的内部。

举一个例子,是反射在工厂模式中的应用:

class LazyImport:
def __init__(self, module_name):
self.module_name = module_name
self.module = None

def __getattr__(self, funcname):
if self.module is None:
self.module = __import__(self.module_name)
print(self.module)
return getattr(self.module, funcname)

string = LazyImport('string')
print(string.ascii_lowercase)

几个重要的API

dir: 列出一个对象所有的方法和属性

>>> a = [1,2,3]
>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append',
'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', ‘sort']

当你对一个你构造的对象使用dir()时,可能会发现列表中的很多属性并不是你定义的。这些属性一般保存了对象的元数据,比如类的__name__属性保存了类名。大部分这些属性都可以修改,不过改动它们意义并不是很大

getattr方法,传入参数是对象和该对象的函数或者属性的名字,返回对象的函数或者属性实例

getattr(a,'sort')
<built-in method sort of list object at 0x1093dfe18>

hasattr(obj, attr): 这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值。

>>> hasattr(a,'sort')
True

setattr(obj, attr, val):
调用这个方法将给obj的名为attr的值的属性赋值为val。例如如果attr为'bar',则相当于obj.bar = val。

callable方法,如果传入的参数是可以调用的函数,则返回true,否则返回false

>>> callable(getattr(a,'sort'))
True

下面这段代码列出对象所有函数:
methodList = [method for method in dir(object) if callable(getattr(object,method))]

globals()和locals()返回的是全局和局部的名字空间。是map,名字:对象。

isinstance(object, classinfo) 检查object是不是classinfo中列举出的类型,返回布尔值

>>> isinstance(a,list)
True
>>> type(a)
<type 'list'>
>>> type(a) == list
True
issubclass判断类参数class是否是类型参数classinfo的子类

class Line:
pass
class RedLine(Line):
pass

class Rect:
pass

print(issubclass(RedLine, Line))
print(issubclass(Rect, Line))

inspect模块提供了一系列函数用于帮助使用自省。下面仅列出较常用的一些函数

检查对象类型
is{module|class|function|method|builtin}(obj):
检查对象是否为模块、类、函数、方法、内建函数或方法。
isroutine(obj):
用于检查对象是否为函数、方法、内建函数或方法等等可调用类型。用这个方法会比多个is*()更方便,不过它的实现仍然是用了多个is*()。

接口与抽象类

接口相当于定义了一个类的样子(所拥有的属性和方法)
抽象类(包含一个或多个抽象方法的类),不能被直接实例化,必须被继承,也就是说它要求子类必须实现某些方法。
从某种意义上来说,他们具有相似的含义。抽象类可以当做接口使用。

由于python 没有抽象类和借口的概念,所以要实现这种功能得abc.py 这个类库

from abc import ABCMeta, abstractmethod
class A:
__metaclass__ = ABCMeta
def __init__(self):
pass
@abstractmethod
def echo(self):
pass

class B(A):
def echo(self,a):
print a
obj = B()
obj.echo(‘abc')

可以看出python 抽象类比较简陋,要求不严格,连抽象方法的个数也不要求。

PHP的面向对象

关键元素(成员方法 成员变量 类常量)

<?php
class a{
const CON="wangwei";
public $var = a;
public function func(){
echo "in func a";
}
}

成员方法

成员变量

类常量

类成员是不是静态的(静态属性,不经过实例化就可以用),即python里的类变量(方法外,看位置)和类方法(classmethod修饰),只看有没有static修饰。static代表的静态意味着整个进程的持续存储的能力。包括局部静态变量。

使用static修饰的成员变量相当于python的类变量。

class abc{
public function func(){
static $b = 0;
$b += 1;
echo $b;
}
public static function func2(){
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
$obj->func();
$obj->func();
$obj2->func();
$obj2->func();
$obj2->func();

123456

实例化与元素引用

$obj = new abc();

引用源5个:类名 self 实例名 $this parent

前两者和最后一个,只能用来引用静态成员
类的内部 self表示类本身。$this表示实例本身。parent表示父类和父类对象(使用parent::引用父类的所有方法,无论是否静态)。

引用符有 ->和::两种,分别用来引用普通成员和静态成员。

php的成员方法没有符号绑定,只能做硬性限制。即静态方法中不可以使用$this引用普通成员。这比较好理解。静态方法不经实例化就可以使用,而$this则是代表实例。

成员变量的定义与引用

普通成员变量(实例变量)
可以在所有方法的外部定义(使用这种),在普通成员方法中定义,甚至可以在类外部定义
可以在类的内部使用$this->引用,在类的外部使用实例->引用。

<?php
class abc{
public $var = 1;
public function func(){
echo $this->var;
$this->var2 = 2;
}
public function func2(){
echo $this->var2;
}

}
$obj = new abc();
$obj->func();
$obj->func2();
echo $obj->var2;

静态成员变量(类变量)
只可以在所有方法的外部定义
在类的类的内部使用self:: $this:: abc:: 引用。在外部使用实例名:: 和类名::引用

<?php
class abc{
public static $var = 1;
public function func(){
echo self::$var;
echo $this::$var;
echo abc::$var;
}

}
$obj = new abc();
$obj->func();
echo $obj::$var;
echo abc::$var;

静态成员变量的改变会影响这个类的所有实例。
class abc{
public $var = 1;
public static $var2 = 2;
public function func(){
$this->var = 3;
self::$var2 = 4;
}

}
$obj = new abc();
$obj2 = new abc();
$obj->func();
echo $obj->var;
echo $obj::$var2;
echo $obj2->var;
echo $obj2::$var2;

class abc{

public static $b = 0;
public function func(){
self::$b += 1;
echo self::$b;
}
public static function func2(){
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
$obj2->func();
成员方法的引用

普通成员方法(实例方法)
可以在类的内部使用$this->引用,在类的外部使用实例->引用。
class abc{
public $var = 1;
public function func(){
print $this->var;
}
public function func2(){
print $this->var;
$this->func();
}

}
$obj = new abc();
$obj->func2();

静态成员方法(类方法)
在类的内部使用self:: abc:: 引用。在外部使用实例名:: 和类名::引用
class abc{
public static $var = 1;
public static function func(){
echo self::$var;
}
public static function func2(){
self::func();
abc::func();
}
}
$obj = new abc();
$obj::func2();
abc::func2();

类常量的定义与访问(值不可改变)
在类的内部使用self:: abc:: 引用。在外部使用实例名:: 和类名::引用
class abc{

const CVAR = 9;
public function func(){
echo self::CVAR;
echo abc::CVAR;
}
}
$obj = new abc();
$obj->func();
echo $obj::CVAR;
echo abc::CVAR;

访问控制

面向对象的一大特性是封装性,即数据隐藏。数据隐藏的对象是:成员变量、成员方法。而类常量是不可隐藏的。隐藏的方法是加关键字:
Public,private,protected
Public:这是默认的类型。公开的。可以随意调用。子类可以集成父类所有的公共成员。
Private:只能在类内,不能在类外调用(即实例化后或者调用静态),也不可被子类继承。
Protected:只能在类内和子类内部调用,是私有数据的折中方案。

class abc{

public function func(){
echo "public func ";
}
private static function func2(){
//私有或者受保护类型定义成静态没有意思 因为外部不可访问
echo "private func ";
}
protected function func3(){
echo "protected func ";
}
public function use_func(){
$this->func();
self::func2();
$this->func3();
}
}
$obj = new abc();
$obj->use_func();
$obj->func();
//PHP Fatal error: Call to private method abc::func2() from context
//$obj->func2();
//PHP Fatal error: Call to protected method abc::func3()
//$obj->func3();

class cd extends abc{
public function sub_func(){
$this->func();
//PHP Fatal error: Call to private method abc::func2() from context
//$this->func2();
$this->func3();
}
}
$obj = new cd();
$obj->sub_func();
$obj->func();

构造与析构

惨绝人寰,如果在子类中不显示的调用父类的构造函数,则不会自动执行父类的。(也难怪,没有理由要求,参数同步)

如果子类没有构造方法,则父类的构造方法会自动执行
class abc{
public function __construct($name,$age){
$this->name = $name;
$this->age = $age;
}
}
class cd extends abc{
}
$obj = new cd('wangwei',25);
echo $obj->name,$obj->age;

如果有的话,则需要显式调用,parent只代表自己的直属父类

class origin {
public function __construct($name,$age){
$this->name_or = $name;
$this->age_or = $age;
}

}
class abc extends origin{
public function __construct($name,$age){
$this->name = $name;
$this->age = $age;
parent::__construct($name,13);
}
}
class cd extends abc{
public function __construct($name){
$this->sub_name = $name;
parent::__construct($name,12);
}
}
$obj = new cd('wangwei');
echo $obj->name,$obj->age;
echo $obj->name_or,$obj->age_or;

继承、多态性(覆盖与重载)

在大多数人看来,多继承并不是一种好的设计方法。php只支持单继承。

无论是调用子类的某个方法、还是在父类中调用某一个方法,都是子类同名(同性、即要么非静态要么静态否则报错)方法覆盖父类的。可以使用parent::直接调用父类。

class abc {
public function func(){
echo 'parent';
}
public function use_func(){
$this->func();
}
}
class cd extends abc{
public function func(){
parent::func();
echo 'subclass';
}
}
$obj = new cd();
$obj->use_func();

parentsubclass

使用__call魔术方法来实现重载。

class A{
public function __call($name, $num){
var_dump($name);
var_dump(count($num));
$func_name = $name.count($num);
$this->$func_name();
}
public function func0(){
echo 'haha';
}
}
$obj = new A();
$obj->func();

自省与反射

反射api是php内建的oop技术扩展,包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。这些oop扩展被称为反射。
通过ReflectionClass,我们可以得到类的以下信息:
1)常量 Contants
2)属性 Property Names
3)方法 Method Names
5)命名空间 Namespace
6)Person类是否为final或者abstract

/article/1208363.html

http://php.net/manual/en/book.reflection.php

/article/8174559.html

接口与抽象类

PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。

继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。

abstract class AbstractClass
{
// 强制要求子类定义这些方法
abstract protected function getValue();
abstract protected function prefixValue($prefix);

// 普通方法(非抽象方法)
public function printOut() {
print $this->getValue() . "\n";
}
}

class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}

public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}

使用接口(interface),你可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。我们可以通过interface来定义一个接口,就像定义一个标准的类一样,但其中定义所有的方法都是空的。接口中定义的所有方法都必须是public,这是接口的特性。

实现
要实现一个接口,可以使用implements操作符。类中必须实现接口中定义的所有方法,否则 会报一个fatal错误。如果要实现多个接口,可以用逗号来分隔多个接口的名称。

Note:

实现多个接口时,接口中的方法不能有重名。

Note:

接口也可以继承,通过使用extends操作符。

常量
接口中也可以定义常量。接口常量和类常量的使用完全相同。 它们都是定值,不能被子类或子接口修改。

/ 声明一个'iTemplate'接口
interface iTemplate
{
const CONST_VAR = 'CONTST';
public function setVariable($name, $var);
public function getHtml($template);
}

// 实现接口
class Template implements iTemplate
{
private $vars = array();

public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}

public function getHtml($template)
{
return $this->vars;
}
}

$obj = new Template();
$obj->setVariable('name','wangwei');
var_dump($obj->getHtml(''));
echo $obj::CONST_VAR;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: