一次 Hyperf 注解失效问题分析

问题环境

PHP: 8.0.13
Swoole: 4.6.2
Hyperf: 2.2.33
运行环境: Docker Desktop on WSL2  

文章会持续修订,转载请注明来源地址:http://her-cat.com/posts/2023/03/02/hyperf-annotation-failure-problem-analysis/

问题背景

有同事说我之前使用注解实现的某个功能有问题,具体表现就是有部分使用了注解的类没有被 Hyperf 收集到注解收集器中,导致出现了不符合预期的结果。

由于这个功能已经运行了一段时间,并且我在自己的电脑(Mac)上测试是正常的,找另外一个跟他同样使用 Windows + Docker 开发的同事进行测试也是正常的,所以可以排除业务代码和环境的问题。

简化后的代码如下:

#[Attribute(Attribute::TARGET_CLASS)]  
class CustomAnnotation extends AbstractAnnotation
{
}  
  
#[CustomAnnotation]  
class Foo
{  
}  
  
#[CustomAnnotation]  
class Bar
{  
}  

在上面的代码中,定义了一个注解类 CustomAnnotation,并且在两个类上使用了这个注解。期望的结果是 FooBar 都能够被 Hyperf 收集到注解收集器中,但实际上只有 Foo 被收集到了。

Foo 和 Bar 分别在不同的文件中,但是都在同一个目录下,该目录下的文件数量有 60+。

于是我俩开始在他的电脑上排查是不是 Hyperf 的问题。

源码分析

在 Hyperf 启动时, ClassLoader 类加载器会扫描项目中所有的类文件,并将元数据(注解与类之间的关系)收集到相应的注解收集器中,如果没有自定义注解收集器,则默认统一收集到 Hyperf\Di\Annotation\AnnotationCollector 类中。

下面是完成收集注解的主要逻辑:

  • 使用 symfony/finder 组件提供的 Finder 类遍历指定目录下所有的 PHP 类文件。
  • 通过反射读取每个文件中的类及其属性、方法上使用的注解。
  • 依次检查这些注解是否实现了 Hyperf\Di\Annotation\AnnotationInterface 接口,该接口定义了三个方法分别用于收集类、方法、属性的元数据。
  • 如果注解实现了该接口,根据注解使用位置调用相应的方法将其收集到注解收集器中。

完成收集后,我们就能使用注解收集器提供的静态方法的获取对应的元数据用于实现一些自定义的逻辑和功能。

第一步就是先检查类文件是否被 Finder 类读取到了,这部分的逻辑在 ReflectionManager::getAllClasses() 静态方法中。

public static function getAllClasses(array $paths): array  
{  
    $finder = new Finder();  
    // 设置读取指定目录下的 PHP 文件
    $finder->files()->in($paths)->name('*.php');  
    $parser = new Ast();  
  
    $reflectionClasses = [];  
    foreach ($finder as $file) {  
        try {  
	        // 解析文件内容获取类名称
            $stmts = $parser->parse($file->getContents());  
            if (! $className = $parser->parseClassByStmts($stmts)) {
	            // 没获取到说明没有定义类
                continue;  
            }
            $reflectionClasses[$className] = static::reflectClass($className);  
        } catch (\Throwable) {  
        }    
    }    
    return $reflectionClasses;  
}

将获取目录下文件的这段代码提出来单独进行测试。由于 Finder 类实现了 IteratorAggregate 接口,所以在上面的代码中可以直接对 Finder 类进行遍历,也可以使用 iterator_to_array() 函数直接获取迭代器的结果。

$finder = new Finder();  
// 设置读取指定目录下的 PHP 文件
$finder->files()->in('出现问题的目录路径')->name('*.php'); 
var_dump(iterator_to_array($finder));

通过观察打印的结果就发现了问题所在:没有读取到 Bar 的类文件。

当时就在想,这么流行的一个组件包总不能出现这么低级的 Bug 吧?抱着怀疑的心态继续分析 Finder 类实现迭代器的代码,最后将问题定位到了 PHP 内置的 RecursiveDirectoryIterator 类上,Finder 类实际上就是对 PHP 的这些类做了一层封装。

RecursiveDirectoryIterator 提供了一个用于递归迭代文件系统目录的功能,用这个类再次进行上面的测试,依然没有读取到 Bar 的类文件。

$iter = new RecursiveDirectoryIterator('出现问题的目录路径');
var_dump(iterator_to_array($iter));

于是,我又一次陷入了怀疑中,难道 PHP 实现的这个类有问题?还得继续看 PHP 的源码?我在犹豫了一会后打开了 Google,抱着肯定有人也遇到过这个问题的想法输入了「RecursiveDirectoryIterator bug」,按下回车,在短暂的页面加载后...

嘿,还真有人已经遇到过这个问题。

真相大白

在前几条搜索结果中,赫然发现有人在 PHP 官方的 Bug 系统反馈了这个问题:RecursiveDirectoryIterator returns incorrect results for Docker Desktop on WSL2,并贴心的附带了可以复现问题的代码。

下面是精简过后的复现代码。

$filesPath = __DIR__.'/files';  
  
if (! mkdir($filesPath) && ! is_dir($filesPath)) {  
    throw new \RuntimeException(sprintf('Directory "%s" was not created', 'files'));  
}  
  
$max = 1;  
$stop = 5000;  
  
// 生成测试文件,模拟目录中文件较多的情况  
foreach(range(1, $stop) as $index) {  
    $message = sprintf("creating %s\n", $index);  
    echo $message;  
    file_put_contents(__DIR__ . '/files/file' . $index, str_repeat('A', 100));  
}  
  
$iter = new \RecursiveDirectoryIterator($filesPath, FilesystemIterator::KEY_AS_PATHNAME|FilesystemIterator::CURRENT_AS_FILEINFO|FilesystemIterator::SKIP_DOTS);  
var_dump(iterator_count($iter));
// 打印出来的数字小于 5000 说明复现成功了

PHP 官方给出了回复:这是 WSL 的 Bug,并提供了相关的 issue:WSL2: Seek of directory entry by lseek does not work on v9fs。里面的实际输出跟我们发现这个问题时的打印结果几乎一模一样,感兴趣的可以去看看。

有人可能会问,lseek() 函数跟 RecursiveDirectoryIterator 类有什么关系吗 ?

当然有!将上面的代码保存到 test.php 文件,然后执行 strace php test.php 命令查看 PHP 代码的系统调用情况。

...省略其他部分...
openat(AT_FDCWD, "/home/ubuntu/files", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 4
fstat(4, {st_mode=S_IFDIR|0775, st_size=135168, ...}) = 0
brk(0x55d84733f000)                     = 0x55d84733f000
getdents(4, /* 1024 entries */, 32768)  = 32752
lseek(4, 0, SEEK_SET)                   = 0
getdents(4, /* 1024 entries */, 32768)  = 32752
getdents(4, /* 1024 entries */, 32768)  = 32768
getdents(4, /* 1024 entries */, 32768)  = 32768
getdents(4, /* 1024 entries */, 32768)  = 32768
getdents(4, /* 906 entries */, 32768)   = 28992
getdents(4, /* 0 entries */, 32768)     = 0
write(1, "int(5000)\n", 10int(5000)
)             = 10
close(3)                                = 0
close(4)                                = 0
...省略其他部分...

可以看到,RecursiveDirectoryIterator 类在底层中调用了 lseek() 函数,它的作用是设置文件偏移量。lseek(4, 0, SEEK_SET) 表示将文件偏移量设置为 0,即文件开头的位置,该函数无法工作会导致下次操作依然使用的是原来的文件偏移量。

Linux 中万物皆为文件,包括目录。

用 PHP 代码来举个例子,这里使用 PHP 的 rewinddir() 函数代替 lseek() 函数,实际上底层调用的还是 lseek() 函数。

$dh = opendir(__DIR__ . '/files');  
  
echo '开始读取目录中的所有文件:' . PHP_EOL;  
while (($file = readdir($dh)) !== false) {  
    echo 'filename:' . $file . PHP_EOL;  
}
  
echo '再次读取目录中的所有文件:' . PHP_EOL;  
// 这时文件偏移量已经到达文件的末尾,再次读取目录将不会有任何输出,模拟 lseek() 函数无法工作的情况 
while (($file = readdir($dh)) !== false) {  
    echo 'filename:' . $file . PHP_EOL;  
}  
  
// 将文件偏移量重置到文件的开头  
rewinddir($dh);  
  
echo '重置偏移量后读取目录中的所有文件:' . PHP_EOL;  
// 与第一次读取的结果相同,模拟 lseek() 函数正常工作的情况
while (($file = readdir($dh)) !== false) {
    echo 'filename:' . $file . PHP_EOL;  
}  
  
closedir($dh);

在 WSL2 以外的系统中运行以上代码,可以得到与预期一致的结果。那么在 WSL2 中运行的结果是什么?

解决问题

当然,最好是 WSL 官方能够修复这个问题,但是从有人提出这个问题到现在已经快三年了依然没有被解决的情况来看,不知道得等到猴年马月。

提问的作者也给出了一种解决方案,开启 Hyper V。但是经过测试后发现开启 Hyper V 依然会出现这个问题,所以最后直接从 WSL2 回滚到 WSL1,从另一种「根本上」解决这个问题。

总结

等等,文章开头不是说已经排除是环境的问题了吗?怎么最后又是环境的问题了?

是的,这是由于我当时并没有问清楚,只是确认了另一个同事是用 Docker 运行的,我怎么也没想到他是本地运行了个虚拟机,然后在虚拟机里面运行 Docker...

当然,后面的源码分析也不是一点作用都没有,至少将问题的范围从 Hyperf 框架缩小到了 Finder 类,再到 RecursiveDirectoryIterator 类。否则直接 Google 搜索「Hyperf 注解失效」是很难找到正确答案的。

在这篇文章中,讲述了我排查「Hyperf 注解失效」问题的过程,整个排查过程看似一气呵成,但实际上要曲折得多,甚至一度觉得这是个玄学问题。

最后,没有 Bug 的程序是不存在的,不要过度迷信那些看似很可靠的系统。

博客地址:她和她的猫,欢迎关注。
本文转载于网络 如有侵权请联系删除

相关文章

  • 国产操作系统有实现弯道超车的可能吗?

    操作系统生态发展是核心竞争力国产操作系统采取了成熟的开源操作系统Linux的技术路线,同时也投入了大量研发,从性能上已经较好的实现了追赶,基本达到了好用阶段。但受制于MacOS或Windows10等操作系统的关键问题不在于技术能力,而在于生态建设。虽然统信软件在今年7月宣布生态适配数量突破50万,成为国内首个突破50万生态适配的操作系统厂商。(截至7月25日,统信软件软硬件兼容适配认证数共计529903款),但与海外的MacOS和Windows操作系统相比,仍然存在数量级上的差距。111.jpeg操作系统适配数量对比22.jpegUOS生态图谱小程序化应用适配桌面操作系统,爆发式增长的可行性?小程序是一种不需要下载安装即可使用的应用。从2017年微信首次推出小程序开始,经过四年发展,各大互联网巨头纷纷推出自己的小程序应用平台,小程序成为真正意义上的“互联网新技术标准”。截至2021年上半年,全网小程序数量突破700万个,其中,微信小程序是行业主流,数量超过430万个,占比高达约61.43%。2021年3月,为了迎合市场受众及开发者的需求,微信宣布桌面版微信也能打开小程序,当时只有小部分

  • .NET Core with 微服务 - 什么是微服务

    微服务是这几年最流行的架构,说起架构不提微服务都不好意思跟人家打招呼。最近想要再梳理一下关于微服务的知识,并且结合本人的一些实践经验来做一些总结与分享。前面会分享一些概念性的东西,后面也会使用.net来实践,一步步完成一个简单的微服务架构的小demo。什么是微服务其实微服务并没有统一的标准定义。微服务是一种软件架构的风格。它首先由大神martinfowler提出,2014年3月25号在他的博客上发表了一篇博客来描述了这种微服务的架构。原文地址(https://www.martinfowler.com/articles/microservices.html)。 相对于传统的单体(Monolithic)架构应用,微服务把单个进程的应用拆分为多个单独部署的服务。每个服务对外提供一些接口来进行服务间的通讯或者对第三方提供功能。每个独立的服务甚至使用自己独立的存储技术,独立的语言技术栈。说到底微服务架构还是贯彻了软件开发中:单一职责、分而治之、解耦等基本理念,只是它把这种理念从类、类库级别提升到了进程级别。 图片引用自https://www.redhat.com/zh/topics/micros

  • 前端导出csv格式时产生中文乱码和异步A标签点击下载解决方案

    导出csv格式时其他都很正常,只有中文容易产生乱码,后来找到了添加BOM头的解决方案:$('#list_csv_export').click(function(){ varurl=$(this).attr('data-url'); varlayer=layui.layer; layer.msg('正在下载订单明细,请耐心等待',{icon:6,time:1500}); layer.load(2); $.get(url,function(data){ if(undefined==data.retCode){ leta=document.createElement('a'); content="ufeff"+data; leturl=window.URL.createObjectURL(newBlob([content],{type:'text/plain,charset=utf-8'})); letfilename='订单明细.csv'; a.hre

  • 音频处理 windows10下python三方库librosa安装教程

    librosa是处理音频库里的opencv,使用python脚本研究音频,先安装三方库librosa。如下通过清华镜像源安装librosa;pipinstall-ihttps://pypi.tuna.tsinghua.edu.cn/simplelibrosaD:\D00_Python3\D00A2_python3.7.3\installpipinstall-ihttps://pypi.tuna.tsinghua.edu.cn/simplelibrosa Lookinginindexes:https://pypi.tuna.tsinghua.edu.cn/simple Processingc:\users\administrator\appdata\local\pip\cache\wheels\6e\d3\47\7582e7e63ee9127f4773adeb8dcd8490771c063e2607354ba0\librosa-0.7.2-py3-none-any.whl Requirementalreadysatisfied:numba=0.43.0ind:\d00_python3\d

  • PHP中的自动加载操作实现方法详解

    本文实例讲述了PHP中的自动加载操作实现方法。分享给大家供大家参考,具体如下:whatis自动加载?或许你已经对自动加载有所了解。简单描述一下:自动加载就是我们在new一个class的时候,不需要手动去写require来导入这个class.php文件,程序自动帮我们加载导入进来。这是php5.1.2(好像是)版本新加入一个功能,他解放了程序员的双手,不需要手动写那么多的require,变得有那么点智能的感觉。自动加载可以说是现代PHP框架的根基,任何牛逼的框架或者架构都会用到它,它发明出来的理由是啥呢?一个字:懒。因为项目越来愈大,相关联的类库文件越来越多,我们不可能再像小项目那样在一个文件中全部手动一个一个require。如何才能自动加载呢?PHP5.2版本更新了自动加载需要的一个魔术方法——__autoload($class_name)正是这个神奇的内置魔术函数,才能让我们这些屌丝偷懒。我们来看下这个如何使用它。1.自动加载的原理以及__autoload的使用自动加载的原理,就是在我们new一个class的时候,PHP系统如果找不到你这个类,就会去自动调用本文件中的__autolo

  • Python开发简单记事本

    摘要:本文是使用Python,结合Tkinter开发简单记事本。  本文的操作环境:ubuntu,Python2.7,采用的是Pycharm进行代码编辑,个人很喜欢它的代码自动补齐功能。  最近很想对python加深学习一下,同时也是想试着做一些东西,今天使用python,结合Tkinter来做一个简单的跨平台记事本。最终实现的记事本如下,也算是麻雀虽小,五脏俱全了,之后也是会继续完善的:  如上图,我们可以看到这个记事本主要分为三个模块:文件,编辑和关于,结合我自身的习惯外加四个toolbar:新建、打开、撤销和保存。下来就我个人构建这个记事本做个总结。一 整体框架构建1.三个主模块的建立  首先,我们先建立上图中的三个主模块,同时,在模块中建立各个模块的功能。先以文件为例:下设功能:新建、打开、保存和另存为,代码如下:#-*-encoding:utf8 fromTkinterimport* root=Tk() root.title('BenbenNode') #createmenu menubar=Menu(root) root.config(menu=me

  • AutoML算法分析(一):基于强化学习的算法

    AutoML是什么顾名思义,Auto:Automated自动的;ML:MachineLearning机器学习.因此AutoML即为自动机器学习。对于机器学习的算法工程师而言,设计适用于特定问题的机器学习模型是一个非常复杂的任务。需要选择相应的神经网络架构、训练过程、正则化方法、超参等,这些都对最终的性能有很大的影响,需要不断尝试。因此深度学习算法工程师也被称为调(炼)参(丹)工程师。AutoML的目标就是使用自动化的、数据驱动方式来做出上述的决策。用户只要提供数据,通过足够的算力,系统自动决定最佳的方案。各个领域专家不再需要苦恼于学习各种机器学习算法。在AutoML领域,当前引起学者关注最多的便是NAS(NeuralArchitectureSearch,网络结构搜索),对应的算法也非常之多。在automl.org上已经列出了两百多篇相关论文:https://www.automl.org/automl/literature-on-neural-architecture-search/对应的算法大致可以分为三类:基于RL(ReinforcementLearning,强化学习)的离散搜索算法

  • Linux设备驱动模型-Uevent

    前言当一个设备动态的加入到系统时候(比如常见的将U盘插入到PC机器上),设备驱动程序就需要动态的检测到有设备插入了系统,就需要将此事件通知到用户层,然后用户层对这一事件做响应的处理,比如加载USB驱动,更新UI等。而将此事件通知到用户层就需要某种机制,典型的就是mdevhotplug和udev。关于udev和mdevhotplug可以在上篇文章有解释。Linux系统对uevent机制的具体实现是建立在设备模型的基础上的,通过kobject_uevent函数实现。在前面的kset小节中提到了注册一个kset的接口,可以在这里习复下。/** *kset_register-initializeandaddakset. *@k:kset. */ intkset_register(structkset*k) { interr; if(!k) return-EINVAL; kset_init(k); err=kobject_add_internal(&k->kobj); if(err) returnerr; kobject_uevent(&k->

  • 【前端】HTML、CSS、JS、PHP 的学习顺序

    原文地址:http://www.th7.cn/web/html-css/201404/29642.shtml侵删如果你有耐心坚持一年以上的话,我会推荐HTML->CSS->JS->PHP的顺序来学习。1.HTML学习:首先学习HTML,HTML作为标记语言是非常容易学的,把w3school上面的教程过一遍就会了,记住要一个个过,千万不要偷懒,一旦开始偷懒,你会越来越偷懒,最后什么都没学成,HTML教程。如果觉得纯书面的太枯燥,可以看视频,这里推荐一下“后盾网html基础(XHTML网页基础教程)”。2.CSS学习:HTML和CSS这两个东西是一套的,建议可以一起学习。一般来说是叫“CSS+DIV”,这是制作出网页的基本外观的东西,学习这个主要要理解“盒子模型”“样式表”这两个东西。还是把w3school上面的教程学一遍,CSS教程。这里也推荐“后盾网的视频(DIV+CSS网页布局)”。3.JS学习:JS学习相比前面两个会难很多,因为JS才是一门正式的编程语言,同样,w3school的教程全过一遍,JavaScript教程。教程过完了就可以买书看了,强烈推荐一本超好入门

  • ​Substrate 环境安装提速文档

    Substrate环境安装提速文档(Mike版,仅限Debian/UbuntuLinux和Macbrew) ================================这是一份提速文档:)这是一篇非官方的Substrate环境安装文档,因为我发现Substrate官方的安装脚本中有以下几个问题:执行了一些重复工作对网络要求较高(可能需要fq)没有使用缓存对于咋们墙内的同学来说,完成一次安装,就像经历了一次地狱。下面切入正题。官方的一键安装脚本是这个:curlhttps://getsubstrate.io-sSf|bash-s复制我们看一下这个脚本里面什么内容:if[["$OSTYPE"=="linux-gnu"]];then if[[`whoami`=="root"]];then MAKE_ME_ROOT= else MAKE_ME_ROOT=sudo fi if[-f/etc/redhat-release];then echo"RedhatLinuxdetected." echo"This

  • opencv 9 -- 轮廓 特征 三

    1最小外接圆函数cv2.minEnclosingCircle()可以帮我们找到一个对象的外切圆。 它是所有能够包括对象的圆中面积最小的一个(x,y),radius=cv2.minEnclosingCircle(cnt) center=(int(x),int(y)) radius=int(radius) img=cv2.circle(img,center,radius,(0,255,0),2)复制2椭圆拟合使用的函数为cv2.ellipse(),返回值其实就是旋转边界矩形的内切圆(椭圆)ellipse=cv2.fitEllipse(cnt) im=cv2.ellipse(im,ellipse,(0,255,0),2)复制3直线拟合我们可以根据一组点拟合出一条直线,同样我们也可以为图像中的白色点拟合出一条直线rows,cols=img.shape[:2] [vx,vy,x,y]=cv2.fitLine(cnt,cv2.DIST_L2,0,0.01,0.01) lefty=int((-x*vy/vx)+y) righty=int(((cols-x)*vy/vx)+y) img=cv2.lin

  • 如何在本地运行查看github上的开源项目

    看中了一款很多星星的github的项目,想把这个项目拉到自己的电脑上运行查看项目效果,该怎么做? 示例:我们今天要看的github项目地址:https://github.com/lzxb/vue-cnode1.克隆项目:gitclone[https://github.com/lzxb/vue-c...](https://github.com/lzxb/vue-cnode.git) 2.安装nodejs 3.安装依赖:npminstall 4.启动服务:npmrundev复制1:找到克隆的路径(本步骤不包含下载.zip的方法)https://github.com/lzxb/vue-cnode.git 图片.png2:把克隆下来的项目放在D盘gitclonehttps://github.com/lzxb/vue-cnode.git复制以管理员身份打开cmd,进入D盘,执行克隆项目到本地的命令图片.png命令行完成之后,D盘多了一个文件夹图片.png3:在项目里安装依赖:npminstall复制使用命令cdvue-cnode进入克隆下来的项目里,安装依赖,不要直接在D盘里安装,这样会出现错误

  • PYTHON用时变马尔可夫区制转换(MARKOV REGIME SWITCHING)自回归模型分析经济时间序列|附代码数据

    全文下载链接:http://tecdat.cn/?p=22617 本文提供了一个在统计模型中使用马可夫转换模型模型的例子,来复现Kim和Nelson(1999)中提出的一些结果。它应用了Hamilton(1989)的滤波器和Kim(1994)的平滑器 %matplotlib inline import numpy as np import pandas as pd import statsmodels.api as sm from pandas_datareader.data import DataReader from datetime import datetime  DataReader(start=datetime(1947, 1, 1), end=datetime(2013, 4, 1)) 复制 相关视频 ** 拓端 ,赞11 ** 拓端 ,赞8 ** 拓

  • php如何抓取网页

    php如何抓取网页 <?php $url="http://www.pc100.net"; $contents=file_get_contents($url); //如果出现中文乱码使用下面代码 //$getcontent=iconv("gb2312","utf-8",$contents); echo$contents; ?>  来自:http://www.thinkphp.cn/topic/28229.html

  • 四元数 Quaternion

    最近在重写自己游戏引擎的场景管理模块,重温了一下有关四元数的一些知识,在此做一下简单的笔记。 四元数可以用来准确地描述三维矢量的旋转,并且可以有效地表达多个旋转操作的叠加,因此在三维游戏引擎的场景管理模块中,四元数具有很重要的意义。   本文为大便一箩筐的原创内容,转载请注明出处,谢谢:http://www.cnblogs.com/dbylk/     一、定义 形如A=ai+bj+ck+d的复数称为四元数,其中i、j、k为虚数(称为四元数的基元),a、b、c、d为实数。 二、常见性质 1.i2=j2=k2=-1 2.ij=k      jk=i        ki=j 3.ij=–ji    jk=–kj     ki=-ki 4.ii*=1    i*=–i  &n

  • 第二次作业(pandas)

    importpandasaspd t=pd.DataFrame(pd.read_excel('C:\\Users\\ASUS\\Desktop\\lw\\python高级设计test\\数据文件\\titanic.xlsx')) s=t['survived'].value_counts() print('存活人数为{}\n死亡人数为{}'.format(s[0],s[1]))复制   一、读入titanic.xlsx文件,按照教材示例步骤,完成数据清洗。 titanic数据集包含11个特征,分别是: Survived:0代表死亡,1代表存活Pclass:乘客所持票类,有三种值(1,2,3)Name:乘客姓名Sex:乘客性别Age:乘客年龄(有缺失)SibSp:乘客兄弟姐妹/配偶的个数(整数值)Parch:乘客父母/孩子的个数(整数值)Ticket:票号(字符串)Fare:乘客所持票的价格(浮点数,0-500不等)Cabin:乘客所在船舱(有缺失)Embark:乘客登船港口:S、C、Q(有缺失) 2.统计乘客中男女性别人数 s=t['sex'].value_counts(

  • mysql中字符查询与替换

    select*fromtablenamewherecolumnlike"%str%"----------------------查询表中的某列里包含某str的行 updateear_bbs_threads_contentsetcontent=replace(content,substring(content,locate('[url=',content),locate('[url=',content)-locate('[/url]',content)),'')----------替换content内容里面以[url=开始并以[/url]结束之间的所有字符 updatetablenamesetcolumn=replace(column,'str','')-------------------替换表中某列里内容为str的字符串 updatetablesetcontent=left(content,locate('str',content)+1)-----------删除表中某列里str后的内容 selecttrim(leading'x'from'xxxadminxxx')-----

  • vue : 无法加载文件C:\\Users\\xxx\\AppData\\Roaming\\npm\\vue.ps1,因为在此系统上禁止运行脚本。

    1.以管理员身份运行PowerShell 2.执行:get-ExecutionPolicy,回复Restricted,表示状态是禁止的 3.执行:set-ExecutionPolicyRemoteSigned 4.选择Y 我以为我只是个程序猿,其实我是只程序狗……

  • .Net实战之反射操作篇

    1、上一讲中描述了反射中常见的类,仅仅是描述类与反射之间的关系。    但是实际是对数据的操作,  在反射中,数据如何操作?  [MyTable("T_UserInfo")] publicclassUserInfo:Person,UserService { privatestaticstringnickname; privateint_age2; privateint_age; [DisplayName("年龄")] publicintAge { get { return_age; } set { _age=value; } } [DisplayName("姓名")] publicstringName{get;set;} publicvoidShowUserInfo() { Console.WriteLine(string.Format("name:{0},age:{1}",Name,_age)); } protectedvoidShowName() { Console.WriteLine("showName:"+Name

  • MIUI5(红米、小米)打开开发者模式

    在miui5系统中系统默认隐藏原生android的开发者模式选项,要想启动该模式需要按照以下操作:设置-关于手机-连续点击安卓版本4下。然后再返回主设置页面下,你会发现开发者选项已经出现。

  • Unity using Sqlite

    Iseeonlineblogstalkingaboutreferencingsqlite3.dll,Mono.Data.Sqlite.dll,System.Data.dll,forexampleSetupDatabase(SQLite)forUnity orusingsomeofthethird-partyimportmethodsunity-3rdparty-sqlite-net orimportingviathird-partypackagemanagementtoolsUnity3D入门:如何管理Unity项目中的NuGet包?使用第三方NuGet包管理器——NuGetForUnity. ButIfoundoutthatUnityhaslibrariesforSqlite.ItisUnity.VisualScripting.Dependencies.Sqlite.SQLiteConnection,Theinformationishere:Unity.VisualScripting.Dependencies.Sqlite.SQLiteConnection SoIsimplymade

相关推荐

推荐阅读